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>
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
# 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` 并重新安装或覆盖资源包
|
||||
Reference in New Issue
Block a user