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>
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 构建。
需要准备:
Gozip- Android SDK
- Android NDK
- Android Studio
常用环境变量:
export ANDROID_SDK_ROOT=/path/to/android-sdk
export ANDROID_NDK_HOME=/path/to/android-ndk
默认 MIN_SDK=26,和当前 Android App 的 minSdk 保持一致。如果你确实需要改动原生层最低版本,可以在执行脚本前自行覆盖:
export MIN_SDK=26
建议先跑环境检查:
./android/scripts/check_env.sh
说明:
- 不需要预先在仓库根目录准备
config.json,prepare_runtime_zip.sh会自动生成默认配置并打包进去。
2. 一键准备运行时资源
如果环境已经准备好,直接执行:
./android/scripts/build_all.sh
它会依次完成:
- 打包
honoka_runtime.zip - 编译
arm64-v8a的libhonokachan.so - 编译
armeabi-v7a的libhonokachan.so - 编译
x86_64的libhonokachan.so
完成后再用 Android Studio 打开 android/ 并构建 APK。
3. 单步脚本
3.1 打包运行时资源
./android/scripts/prepare_runtime_zip.sh
3.2 编译 Go 动态库
需要使用 Android NDK 的 clang 作为 CC,并启用 c-shared。
脚本方式:
./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 示例:
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 示例:
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 示例:
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.dbassets/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-chansettings.listen_port:8080settings.cdn_server:http://127.0.0.1:8080/staticsettings.reload_token:""settings.unlock_all_special_rotation:false
手工打包命令:
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. 运行流程
- 准备好
libhonokachan.so - 准备好
honoka_runtime.zip - 用 Android Studio 构建并安装 APK
- 打开 App
- 首次进入时按系统提示授予通知权限
- 根据需要处理“所有文件访问权限”与数据目录选择
- 如有需要,打开“解锁全部日替”开关
- 点击“启动服务”
- 用“刷新状态”确认当前进程健康状态正常
说明:
- 如果系统尚未将本应用加入电池优化白名单,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/reloadHTTP 接口只用于外部调用,仍然保留 token 校验- 无论通过 JNI 还是 HTTP,reload 都只会重新加载运行时目录中的
config.json - 它不会重新解压
honoka_runtime.zip - 如果你更新了数据库、证书、serverdata、模板等资源,需要重新生成
honoka_runtime.zip并重新安装或覆盖资源包