docs: 新增「初次部署(从零跑起来)」;去混淆字符与真实内网信息
- README.md / README.txt 新增初次部署章节:前置条件(JDK 17 / SDK 34 / Gradle 8.9)、 获取代码与本地配置(local.properties 不入仓)、Debug/Release 构建、装到手机与相机权限、 连接配置(只连 Windows 客户端,默认 8765 / /pose)、第一次跑通、自检清单、常见问题 - 单测数量按实际(30 项)修正 - 三库同源契约:':=' 写法修回、去掉真实 MariaDB 版本串 - 全文去除易被 Gitea 判为「模棱两可 Unicode」的字符 - 本机 JDK 路径与内网示例地址改占位符 / RFC 5737 文档网段
This commit is contained in:
@@ -9,11 +9,11 @@ OpenDance Studio **阶段三**:Android 辅助 App(Kotlin + CameraX + MediaPi
|
||||
|
||||
## 状态
|
||||
|
||||
**阶段三已完成**:T-AND-001 → T-AND-009 全部落地。
|
||||
**阶段三已完成**:T-AND-001 -> T-AND-009 全部落地。
|
||||
|
||||
| 验收项 | 结果 |
|
||||
|---|---|
|
||||
| 单元测试 | ✓ **25 项全部通过**(JVM,无需设备) |
|
||||
|--|--|
|
||||
| 单元测试 | ✓ **30 项全部通过**(JVM,无需设备) |
|
||||
| **跨端金标准** | ✓ Kotlin 序列化器与 Web 端 TypeScript 实现**逐位一致** |
|
||||
| `./gradlew assembleRelease` | ✓ 生成**已签名** APK(28.6 MB) |
|
||||
| 签名校验 | ✓ `apksigner` 退出码 0,v2 方案,CN=OpenDance Studio Dev |
|
||||
@@ -28,17 +28,184 @@ export JAVA_HOME=/path/to/jdk17 # AGP 8.7.3 要求
|
||||
export ANDROID_HOME=/path/to/android-sdk
|
||||
|
||||
bash build/gen-keystore.sh # 生成发布签名(首次;不入库,请自行备份)
|
||||
bash build/verify.sh # 单元测试 → 构建 → 产物校验
|
||||
bash build/verify.sh # 单元测试 -> 构建 -> 产物校验
|
||||
bash build/build-apk.sh all # 仅构建 debug + release
|
||||
bash build/package-release.sh # 打发布包
|
||||
```
|
||||
|
||||
详细使用说明见 [`README.txt`](README.txt),工程约束见 [`DSH.md`](DSH.md)。
|
||||
详细使用说明见 [`README.txt`](README.txt),工程约束见 [`DSH.md`](DSH.md),
|
||||
第一次上手请看下文的「初次部署(从零跑起来)」一节。
|
||||
|
||||
## 初次部署(从零跑起来)
|
||||
|
||||
本节面向**第一次拿到本仓库**、要在真机上把 App 跑起来的人。
|
||||
下文出现的地址、路径、口令一律是**占位符或文档示例值**,请替换为你自己的值,
|
||||
并确保这些值**不要提交进仓库**。
|
||||
|
||||
### 1. 前置条件
|
||||
|
||||
| 项 | 要求 | 依据 |
|
||||
|--|--|--|
|
||||
| JDK | 17 或更高(AGP 8.7.3 要求;`build/build-apk.sh` 会校验主版本 >= 17) | `gradle.properties` / `build/build-apk.sh` |
|
||||
| Gradle | 8.9,仓库自带 wrapper(`./gradlew`),本机无需另装 | `gradle/wrapper/gradle-wrapper.properties` |
|
||||
| Android SDK | `compileSdk 34` / `minSdk 29`(Android 10+)/ `targetSdk 34`;需 `platforms;android-34` 与 `build-tools;34.0.0` | `app/build.gradle.kts` |
|
||||
| 设备 | **需要真机**(Android 10 及以上、带摄像头);模拟器只作有限调试 | `app/build.gradle.kts` 的 ABI 过滤 |
|
||||
|
||||
真机与模拟器的差别:
|
||||
|
||||
- release 包只打包 `arm64-v8a` + `armeabi-v7a`,**无法安装到 x86/x86_64 模拟器**。
|
||||
- debug 包额外含 `x86`/`x86_64`,可以装进模拟器,但模拟器相机是虚拟场景,
|
||||
CameraX 取到的是合成画面,MediaPipe 的姿态识别效果不能代表真机,
|
||||
也不能作为「第一次跑通」的验收依据。
|
||||
- 结论:**姿态识别与联调验收请用真机**。
|
||||
|
||||
权限:运行页需要**相机权限**(运行时授予)。另外 `INTERNET`、`ACCESS_NETWORK_STATE`、
|
||||
`ACCESS_WIFI_STATE` 为普通权限,安装时自动授予,用于 WebSocket 推流与局域网扫描。
|
||||
|
||||
### 2. 获取代码与本地配置
|
||||
|
||||
```bash
|
||||
git clone https://<Gitea 地址>/<owner>/opendance-android.git
|
||||
cd opendance-android
|
||||
```
|
||||
|
||||
`local.properties` 是**本地文件**(已在 `.gitignore` 中,不会入库),
|
||||
只需写 Android SDK 路径:
|
||||
|
||||
```properties
|
||||
sdk.dir=<Android SDK 路径>
|
||||
```
|
||||
|
||||
`build/build-apk.sh` 在该文件缺失时会依据 `ANDROID_HOME` / `ANDROID_SDK_ROOT`
|
||||
自动生成它;也可以手动创建。**不要提交它**,每台机器路径不同。
|
||||
|
||||
签名材料同样**不入库**(`.gitignore` 已忽略 `keystore/`、`*.jks`、`*.keystore`、
|
||||
`keystore.properties`):
|
||||
|
||||
- 仓库里**没有私钥**,只有生成脚本与说明。
|
||||
- 首次发布前执行 `bash build/gen-keystore.sh`,生成
|
||||
`keystore/opendance-release.jks` 与 `keystore.properties`
|
||||
(含 `storeFile`、`keyAlias` 与两个口令字段)。**请自行备份,且不要提交**。
|
||||
- `app/build.gradle.kts` 的签名取值优先级:
|
||||
`keystore.properties` -> 环境变量(`KEYSTORE_PATH` / `KEYSTORE_PASSWORD` /
|
||||
`KEY_ALIAS` / `KEY_PASSWORD`)-> 回退 debug 签名。
|
||||
回退只为让 CI 也能产出已签名 APK;正式发布必须提供自己的 keystore。
|
||||
|
||||
### 3. 首次构建
|
||||
|
||||
```bash
|
||||
export JAVA_HOME=<JDK 17 路径>
|
||||
export ANDROID_HOME=<Android SDK 路径>
|
||||
|
||||
# 方式一:仓库脚本(推荐,构建后自动校验产物)
|
||||
bash build/build-apk.sh debug # 仅 debug
|
||||
bash build/build-apk.sh release # 仅 release
|
||||
bash build/build-apk.sh all # 两者都构建
|
||||
|
||||
# 方式二:直接调用 Gradle
|
||||
./gradlew assembleDebug
|
||||
./gradlew assembleRelease
|
||||
```
|
||||
|
||||
产物路径与文件名(固定):
|
||||
|
||||
| 变体 | 产物 | 说明 |
|
||||
|--|--|--|
|
||||
| debug | `app/build/outputs/apk/debug/app-debug.apk` | applicationId 带 `.debug` 后缀,含 x86/x86_64 |
|
||||
| release | `app/build/outputs/apk/release/app-release.apk` | 仅 arm64-v8a + armeabi-v7a,开启 R8 |
|
||||
|
||||
发布整包(APK + `THIRD_PARTY.md` + `README.txt` + `contracts/`)用
|
||||
`bash build/package-release.sh`,输出在 `dist-release/`。
|
||||
|
||||
### 4. 安装到手机
|
||||
|
||||
```bash
|
||||
# 用 adb(手机需开启 USB 调试)
|
||||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
adb install -r app/build/outputs/apk/release/app-release.apk
|
||||
```
|
||||
|
||||
也可以直接把 APK 拷到手机,在系统设置中允许「安装未知来源应用」后点击安装。
|
||||
|
||||
首次进入运行页会弹出**相机权限**请求;拒绝后运行页显示
|
||||
「需要相机权限才能进行姿态识别」,可点「授予相机权限」重新申请,
|
||||
或到系统「设置 -> 应用 -> OpenDance -> 权限」中手动开启。
|
||||
|
||||
### 5. 配置与联通(填 Windows 客户端地址)
|
||||
|
||||
本 App **只连接 Windows 客户端,不访问 Web 服务端**:地址由
|
||||
`AppSettings.webSocketUrl()` 拼成 `ws://<host>:<port>/pose`,子协议 `opendance.pose.v1`。
|
||||
|
||||
连接页字段(对应 `data/AppSettings.kt`):
|
||||
|
||||
| 界面字段 | 设置项 | 默认值 | 说明 |
|
||||
|--|--|--|--|
|
||||
| IP 地址 | `host` | RFC 5737 文档网段占位地址(源码为 `192.0.2.100`,**非真实主机**) | 填 Windows 电脑的局域网 IP |
|
||||
| 端口 | `port` | `8765` | 与 Windows 客户端监听端口一致 |
|
||||
| 目标帧率(FPS) | `targetFps` | `30` | 取值范围 5~60 |
|
||||
| 分辨率 | `resolution` | `640x480` | 可选 `480x360` / `640x480` / `1280x720` |
|
||||
| 镜像画面 | `mirror` | 开 | 只影响显示与 `flags.bit0`,发送的是未镜像坐标 |
|
||||
| 仅上半身模式 | `upperBodyOnly` | 关 | 下半身置信度置 0,接收端插值 |
|
||||
|
||||
填写示例(占位符,请替换):
|
||||
|
||||
```
|
||||
IP 地址:192.0.2.10 # RFC 5737 文档示例网段,实际填你电脑的局域网 IP
|
||||
端口: 8765
|
||||
```
|
||||
|
||||
不想手填 IP 时,可点「扫描局域网」:App 取本机 WiFi IPv4 地址后推导 `/24` 网段,
|
||||
以 32 并发、单机 300ms 超时探测该网段内开放 8765 端口的主机(约 2~3 秒),
|
||||
结果按延迟升序展示,点候选即可填入。也可以先点「测试连接」确认端口可达。
|
||||
|
||||
### 6. 第一次跑通
|
||||
|
||||
数据流是**单向**的:`手机 -> Windows 客户端`;手机端不连 Web 服务端。
|
||||
|
||||
1. 手机与 Windows 电脑连到**同一个局域网 / WiFi**(路由器不要开 AP 隔离)。
|
||||
2. 在电脑上启动 OpenDance Studio Windows 客户端,确认它在监听 8765 端口。
|
||||
3. 手机打开 App,连接页填入电脑 IP(或用「扫描局域网」自动发现),点「测试连接」。
|
||||
4. 点「开始推流」,首次会申请相机权限,允许后进入运行页。
|
||||
5. 站到画面中央,屏幕上出现骨架叠加。
|
||||
|
||||
成功标志:
|
||||
|
||||
| 位置 | 可观察现象 |
|
||||
|--|--|
|
||||
| App 运行页 | 「连接状态」为**已连接**(或短暂「连接中」后转「已连接」) |
|
||||
| App 运行页 | 「目标」显示 `ws://<IP>:<端口>/pose`;服务端返回 welcome 后出现「会话」ID |
|
||||
| App 运行页 | 「已发送」帧数持续增长;「发送帧率」「识别帧率」大于 0;「推理耗时」为毫秒数 |
|
||||
| App 运行页 | 「人体识别」为**已识别**,叠加层出现骨架(黄点 = 置信度好,红点 = 置信度低) |
|
||||
| Windows 客户端 | 显示已连接并收到关键点,VRM 模型随动作实时动起来 |
|
||||
|
||||
### 7. 首次部署自检清单
|
||||
|
||||
| # | 命令 | 预期输出 |
|
||||
|--|--|--|
|
||||
| 1 | `./gradlew testDebugUnitTest` | `BUILD SUCCESSFUL`,全部用例通过;报告在 `app/build/reports/tests/testDebugUnitTest/index.html` |
|
||||
| 2 | `bash build/build-apk.sh debug` | 末尾打印 `✓ Debug APK:app/build/outputs/apk/debug/app-debug.apk` 及其大小与签名信息 |
|
||||
| 3 | `bash build/build-apk.sh all` | debug 与 release 均 `✓`;release 显示 `已签名(APK Signature Scheme v2)` |
|
||||
| 4 | `$ANDROID_HOME/build-tools/34.0.0/apksigner verify app/build/outputs/apk/release/app-release.apk` | 退出码 0(无输出即通过) |
|
||||
| 5 | `unzip -l app/build/outputs/apk/release/app-release.apk \| grep pose_landmarker_lite.task` | 列出 `assets/pose_landmarker_lite.task`(约 5.7 MB,Stored 未压缩) |
|
||||
| 6 | `adb install -r app/build/outputs/apk/debug/app-debug.apk` | `Success` |
|
||||
|
||||
其中 `KeypointFrameTest.goldenVectorMatchesWebImplementation` 必须通过:
|
||||
它断言本端 Kotlin 序列化器与 Web 端 TypeScript 实现产出**逐位相同**的 239 字节帧。
|
||||
|
||||
### 8. 常见问题(首次部署)
|
||||
|
||||
| 现象 | 可能原因 | 处理 |
|
||||
|--|--|--|
|
||||
| 相机黑屏,或一直停在「需要相机权限才能进行姿态识别」 | 相机权限被拒绝或已被永久拒绝;也可能相机被其他 App 占用 | 在运行页点「授予相机权限」;若不再弹窗,去系统「设置 -> 应用 -> OpenDance -> 权限」手动开启,并关闭占用相机的其它 App |
|
||||
| 相机预览正常但「人体识别」一直是「未识别到人体」 | 光线不足、人不在画面内、或分辨率/模式不合适 | 站到画面中央并改善光照;确认分辨率不是过低;观察「推理耗时」是否异常大 |
|
||||
| 「连接状态」一直「已断开 / 重连中」,「测试连接」失败 | IP 或端口填错;Windows 客户端未启动;防火墙拦截入站 8765;手机与电脑不在同一网段(AP 隔离) | 核对连接页「本机地址」与电脑 IP 是否同网段;启动客户端;在 Windows 防火墙放行 OpenDance 的 TCP 8765 入站;关闭路由器 AP 隔离 |
|
||||
| 「扫描局域网」扫不到主机 | 手机未连 WiFi、路由器 AP 隔离、或客户端未监听 8765 | 确认手机连的是 WiFi 而非蜂窝;改为手动填 IP;检查客户端监听端口与防火墙 |
|
||||
| 点「开始推流」立即闪退,或提示 MediaPipe 初始化失败 | MediaPipe 原生库加载 / 静态初始化失败抛出的是 **Error** 子类(`UnsatisfiedLinkError`、`ExceptionInInitializerError`),不是 `Exception`;常见诱因是 ABI 不匹配(例如把只含 arm 的 release 包装到 x86 模拟器)或模型资源缺失 | 改用与设备 ABI 匹配的包(真机用 release,模拟器调试用 debug);确认 APK 内含 `assets/pose_landmarker_lite.task`。仓库已在 `PoseDetector.initialize()` 中 `catch Throwable` 兜底并把原因显示在运行页;`CrashLogger` 会把未捕获堆栈写入应用私有目录,并在连接页展示,可截图反馈,或用 `adb logcat -s PoseDetector` 查看 |
|
||||
|
||||
## 技术栈
|
||||
|
||||
| 组件 | 选型 |
|
||||
|---|---|
|
||||
|--|--|
|
||||
| 语言 | Kotlin 2.0.21 |
|
||||
| 构建 | AGP 8.7.3 + Gradle 8.9 + JDK 17 |
|
||||
| 相机 | CameraX 1.4.0 |
|
||||
@@ -68,12 +235,12 @@ app/src/main/java/com/opendance/studio/
|
||||
|
||||
## 依赖的契约(opendance-web v0.1.0 冻结)
|
||||
|
||||
- [`contracts/websocket-keypoints.md`](contracts/websocket-keypoints.md) — 239 字节关键点帧(本端为**发送方**)
|
||||
- [`contracts/skeleton-topology.md`](contracts/skeleton-topology.md) — MediaPipe 33 点索引与坐标语义
|
||||
- [`contracts/open-source-credits.md`](contracts/open-source-credits.md) — 关于页必须展示的开源声明
|
||||
- [`contracts/websocket-keypoints.md`](contracts/websocket-keypoints.md) - 239 字节关键点帧(本端为**发送方**)
|
||||
- [`contracts/skeleton-topology.md`](contracts/skeleton-topology.md) - MediaPipe 33 点索引与坐标语义
|
||||
- [`contracts/open-source-credits.md`](contracts/open-source-credits.md) - 关于页必须展示的开源声明
|
||||
|
||||
`contracts/` 是**只读快照**。任何跨端契约变更必须先回到
|
||||
[`opendance-web`](https://gitea.kasugano.cn/zijunchiang/opendance-web) 修改并提升版本。
|
||||
[`opendance-web`](https://<Gitea 地址>/<owner>/opendance-web) 修改并提升版本。
|
||||
|
||||
## 跨端一致性:金标准字节向量测试
|
||||
|
||||
|
||||
Reference in New Issue
Block a user