Files
honoka-chan/android/README.md
T
YumeMichi 91bbc7f607 Add Android control app and embedded server
Inspired by HNKServer/honoka-chan-apk-server, but implemented as a JNI-based embedded runtime instead of the original approach.

- Add an Android Compose controller app that starts and stops honoka-chan through JNI, shows runtime and health status, manages data directory mounting, and supports backup import/export for data.db
- Add an embeddable Go server entrypoint plus JNI-exported status, health, and reload hooks so desktop and Android builds share the same startup and shutdown path
- Add Android build scripts, runtime packaging, and project documentation, including generated default config.json content for honoka_runtime.zip
- Add runtime bundle hash tracking so updated honoka_runtime.zip assets are automatically redeployed while preserving config.json and user data files
- Add in-app service settings for unlock_all_special_rotation with immediate config persistence and automatic reload when the service is running
- Split SQLite driver selection by platform, using go-sqlite3 on Android and modernc.org/sqlite elsewhere to avoid Android x86_64 seccomp crashes
- Update startup, database initialization, and system health/reload handlers to support the embedded runtime and Android control flow

Signed-off-by: Sean Du <do4suki@gmail.com>
2026-06-12 19:17:21 +08:00

247 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Android 控制端
这个目录提供一个 Kotlin/Compose Android App,用来把 `honoka-chan` 作为本地动态库加载,并通过 JNI 控制启停。
## 当前功能
- 启动 / 停止本地 `honoka-chan` 服务
- 直接通过 JNI 查询运行状态、健康信息,并在修改配置后即时重载
- 将指定目录以符号链接方式挂到运行时 `/static/Android`
- 支持导出 / 导入用户数据备份(`data.db`
- 使用前台服务 + `PARTIAL_WAKE_LOCK` 做后台保活
- 首次启动时解压运行时资源包到应用私有目录
- 打包运行时资源时自动生成默认 `config.json`
- 检测到未加入电池优化白名单时,仅弹出说明提示,不强制跳转系统设置
## 目录约定
- Go 动态库放到:`android/app/src/main/jniLibs/<abi>/libhonokachan.so`
- 运行时资源包放到:`android/app/src/main/assets/honoka_runtime.zip`
目前工程里已经预建了:
- `android/app/src/main/jniLibs/arm64-v8a/`
- `android/app/src/main/jniLibs/armeabi-v7a/`
- `android/app/src/main/jniLibs/x86_64/`
- `android/app/src/main/assets/`
## 1. 开发环境要求
Linux 下建议直接使用 `android/scripts/*.sh` 先准备运行时资源与 JNI 动态库,最终 APK 由 Android Studio 构建。
需要准备:
- `Go`
- `zip`
- Android SDK
- Android NDK
- Android Studio
常用环境变量:
```bash
export ANDROID_SDK_ROOT=/path/to/android-sdk
export ANDROID_NDK_HOME=/path/to/android-ndk
```
默认 `MIN_SDK=26`,和当前 Android App 的 `minSdk` 保持一致。如果你确实需要改动原生层最低版本,可以在执行脚本前自行覆盖:
```bash
export MIN_SDK=26
```
建议先跑环境检查:
```bash
./android/scripts/check_env.sh
```
说明:
- 不需要预先在仓库根目录准备 `config.json``prepare_runtime_zip.sh` 会自动生成默认配置并打包进去。
## 2. 一键准备运行时资源
如果环境已经准备好,直接执行:
```bash
./android/scripts/build_all.sh
```
它会依次完成:
1. 打包 `honoka_runtime.zip`
2. 编译 `arm64-v8a``libhonokachan.so`
3. 编译 `armeabi-v7a``libhonokachan.so`
4. 编译 `x86_64``libhonokachan.so`
完成后再用 Android Studio 打开 `android/` 并构建 APK。
## 3. 单步脚本
### 3.1 打包运行时资源
```bash
./android/scripts/prepare_runtime_zip.sh
```
### 3.2 编译 Go 动态库
需要使用 Android NDK 的 clang 作为 `CC`,并启用 `c-shared`
脚本方式:
```bash
./android/scripts/build_go_android.sh arm64-v8a
./android/scripts/build_go_android.sh armeabi-v7a
./android/scripts/build_go_android.sh x86_64
```
手工方式,`arm64-v8a` 示例:
```bash
export ANDROID_NDK_HOME=/path/to/android-ndk
export CC="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android26-clang"
CGO_ENABLED=1 GOOS=android GOARCH=arm64 \
go build -buildmode=c-shared \
-o android/app/src/main/jniLibs/arm64-v8a/libhonokachan.so \
./cmd/honoka-android
```
手工方式,`armeabi-v7a` 示例:
```bash
export ANDROID_NDK_HOME=/path/to/android-ndk
export CC="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi26-clang"
CGO_ENABLED=1 GOOS=android GOARCH=arm GOARM=7 \
go build -buildmode=c-shared \
-o android/app/src/main/jniLibs/armeabi-v7a/libhonokachan.so \
./cmd/honoka-android
```
手工方式,`x86_64` 示例:
```bash
export ANDROID_NDK_HOME=/path/to/android-ndk
export CC="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android26-clang"
CGO_ENABLED=1 GOOS=android GOARCH=amd64 \
go build -buildmode=c-shared \
-o android/app/src/main/jniLibs/x86_64/libhonokachan.so \
./cmd/honoka-android
```
说明:
- 这里直接使用 `./cmd/honoka-android`,不需要额外 JNI 头文件。
- Android App 自带的 `honoka_android.cpp` 会在运行时通过 `dlopen("libhonokachan.so")` 找到导出的 `ServerStart` / `ServerStop` / `ServerStatusJSON` 等符号。
- `go build -buildmode=c-shared` 会顺带生成 `libhonokachan.h`,脚本会自动删除这个头文件,因为当前 Android 工程并不会使用它。
## 4. 打包运行时资源
`honoka-chan` 在 Android 上仍然按仓库根目录结构读取文件,所以 zip 包内必须直接包含这些顶层条目:
- `assets/`
- `static/`
- `config.json`
最小建议内容:
- `assets/main.db`
- `assets/serverdata/`
- `assets/certs/`
- `static/templates/`
- `static/css/`
- `static/js/`
- `static/images/`
- `static/font/`
- `config.json`
如果你希望把已有用户数据也一起带进去,可以额外放入:
- `assets/data.db`
`prepare_runtime_zip.sh` 会自动生成一份默认配置,内容等同于服务端 `config.DefaultConfigs()`
- `app_name`: `honoka-chan`
- `settings.listen_port`: `8080`
- `settings.cdn_server`: `http://127.0.0.1:8080/static`
- `settings.reload_token`: `""`
- `settings.unlock_all_special_rotation`: `false`
手工打包命令:
```bash
zip -r android/app/src/main/assets/honoka_runtime.zip assets static config.json
```
注意:
- 压缩包根目录下必须直接看到 `assets``static``config.json`,不要多包一层目录。
- App 会记录 `honoka_runtime.zip` 的内容哈希;如果资源包发生变化,下次启动服务或刷新挂载时会自动重新解压。
- 自动重新解压时会保留运行时目录里现有的 `config.json`,避免覆盖用户已经修改过的服务设置。
- 自动重新解压时也会保留运行时目录里现有的 `assets/data.db` 及其 `-wal/-shm`,避免覆盖用户数据。
## 5. 构建 APK
当前不再提供命令行脚本构建 APK,直接使用 Android Studio 打开 `android/` 构建即可。
如果你主要用 Android Studio 模拟器测试,建议保留 `x86_64` ABI;很多模拟器镜像不会加载仅包含 ARM so 的 APK。
## 6. 数据目录
App 会把你选择的目录挂到运行时:
- `/static/Android`
实现方式不是 Linux bind mount,而是:
- 在应用私有运行时目录中创建 `static/Android -> 你的目录` 的符号链接
默认数据目录:
- `/sdcard/Download/data`
如果 Android 版本较高,点击“选择目录”时会先跳转系统设置申请“所有文件访问权限”,授权后自动继续打开目录选择器。
## 7. 备份导入导出
- 备份导出和导入的对象都是运行时目录里的 `assets/data.db`
- 执行前需要先停止服务,避免数据库仍在使用中
- 导出前 App 会先执行一次 `wal_checkpoint(TRUNCATE)`,这样单独导出的 `data.db` 就是完整的
- 导入备份时会提示“将覆盖当前用户数据,是否继续”
## 8. 运行流程
1. 准备好 `libhonokachan.so`
2. 准备好 `honoka_runtime.zip`
3. 用 Android Studio 构建并安装 APK
4. 打开 App
5. 首次进入时按系统提示授予通知权限
6. 根据需要处理“所有文件访问权限”与数据目录选择
7. 如有需要,打开“解锁全部日替”开关
8. 点击“启动服务”
9. 用“刷新状态”确认当前进程健康状态正常
说明:
- 如果系统尚未将本应用加入电池优化白名单,App 只会弹出说明提示,不会自动跳转设置页。
- 即使没有加入白名单,服务仍然可以启动。
- 在部分国产系统上,如果退到后台后服务仍会断开,除了忽略电池优化、自启动和后台运行,还可能需要在最近任务中手动锁定本应用。
## 9. 其他说明
- 这个 Android 子目录默认只提交源码与脚本;`gradlew``gradle/`、构建产物、`jniLibs/``assets/` 等本地生成内容当前都被 `android/.gitignore` 排除了。
- 如果你希望把 Gradle wrapper 一起纳入仓库,可以自行调整 `android/.gitignore`
- 前台服务会持有 `PARTIAL_WAKE_LOCK`,停止服务时会释放。
- App 内部的状态、健康检查、配置重载都直接通过 JNI 调用 Go 导出符号。
- `/system/health``/system/reload` 仍然保留,方便外部调试、脚本探活和浏览器访问。
- Android Studio 升级到 AGP `8.11.2` 后,如果项目放在 Samba 映射盘上,可能会出现 `Cannot create directory .../merged.dir/values` 之类的 AAPT 目录创建失败。这个更像文件系统兼容问题,建议改回本地磁盘构建。
## 10. Reload 与资源更新说明
- App 内部修改服务设置时会直接在进程内执行 reload
- `/system/reload` HTTP 接口只用于外部调用,仍然保留 token 校验
- 无论通过 JNI 还是 HTTP,reload 都只会重新加载运行时目录中的 `config.json`
- 它不会重新解压 `honoka_runtime.zip`
- 如果你更新了数据库、证书、serverdata、模板等资源,需要重新生成 `honoka_runtime.zip` 并重新安装或覆盖资源包