ReconnectPolicyTest(11 项)验的是**纯函数**「给定关闭码应该怎么退避」,而 ⑦
「连接频繁重连」的缺陷往往出在**调用点**。新增 PoseWebSocketReconnectTest(8 项):
用 MockWebServer.withWebSocketUpgrade 在 JVM 上起**真实** WebSocket 服务端
(能按 §3.2 回 welcome、按 §3.6 回 pong、按指定关闭码关连接),无需设备即可覆盖
「该重连的」与「不该重连的」两条分支。Robolectric 只用来提供 android.os.Build 桩。
它抓到的真实缺陷(本版修复):
上一次会话的关闭码会污染下一次会话,导致重新开始推流后失去自动重连能力。
复现:第一次连接被对端以 1001 关闭(按 §7 不重连,正确)→ 用户再点「开始推流」
→ 这一次是网络层失败(服务端还没起来),按 §7 **应当**重连 → 但 connect() 只重置了
reconnectCounter 与 lastPongAtMs,**没有清 lastCloseCode** → scheduleReconnect()
仍按 1001 判定 → 直接 stopped,**再也不会自动重连**。
测试失败信息(修复前):
✗ 上一次 1001 关闭后重新连接,遇到网络失败仍应按策略重连
AssertionError: 当前 state=STOPPED,lastCloseCode=1001,attempts=0
修复(PoseWebSocketClient.kt):
* connect():清掉会话级关闭状态(lastCloseCode=0、lastCloseReason="");
* onFailure():记录 recordClose(0, …)(契约 §7 中 0=未收到关闭帧 → 应重连),
顺带让界面「最近一次断开原因」显示本次失败而不是旧值。
用例清单:握手→streaming 与 welcome 保活参数解析、按协商间隔发 ping 收 pong、
1011 按退避重连且等待落在策略 ±20% 区间、成功后计数清零、1001/1002 不重连、
主动断开不重连、上一次 1001 后再连遇网络失败仍应重连(本次回归锁)、
服务端未启动时 1.5s 内 ≤3 次尝试(退避生效)。
实测:./gradlew :app:testDebugUnitTest → 6 个测试类 / 64 项用例 / 0 失败
(原 56 项);build/verify.sh 全绿(签名、APK 版本元数据 0.2.1/201、native 库体检、
ABI、R8、dex)。版本号 0.2.0/200 → 0.2.1/201。
踩坑:第一版 MockWebServer 夹具的 101 响应没有回显 Sec-WebSocket-Protocol
(RFC 6455 要求),客户端记录的 negotiatedProtocol 为空导致断言失败 —— 那是
**夹具**不合规而非客户端问题,已在夹具里补上。写「真实对端」夹具时要按 RFC/契约
把服务端做对,否则会把夹具缺陷误判成产品缺陷。
105 lines
3.4 KiB
YAML
105 lines
3.4 KiB
YAML
project: opendance-android-app
|
||
version: 0.2.1
|
||
repository: opendance-android
|
||
default_branch: main
|
||
platform: Android 10+(minSdk 29 / targetSdk 34)
|
||
contracts_version:
|
||
rest-api: 0.5.0
|
||
websocket-keypoints: 0.2.0
|
||
data-models: 0.7.0
|
||
skeleton-topology: 0.2.0
|
||
model-formats: 0.5.0
|
||
database: 0.4.0
|
||
|
||
# 契约变更流程(文档 7.2):
|
||
# 任何跨端契约变更必须先在 opendance-web 修改 contracts/ 并提升版本,
|
||
# 再在本项目创建适配任务。本项目内的 contracts/ 只是只读快照。
|
||
|
||
dependencies:
|
||
- name: opendance-windows
|
||
kind: websocket-producer
|
||
contract: contracts/websocket-keypoints.md
|
||
usage: 本端为**发送方**,主动连接 Windows 客户端的 ws://<host>:8765/pose
|
||
- name: opendance-web
|
||
kind: contract-source
|
||
contract: contracts/skeleton-topology.md
|
||
usage: 33 点索引顺序与坐标语义(MediaPipe 原生顺序即契约顺序)
|
||
|
||
tasks:
|
||
- id: T-AND-001
|
||
file: tasks/001-scaffold.md
|
||
scope: Android Studio 项目骨架
|
||
status: done
|
||
- id: T-AND-002
|
||
file: tasks/002-camera-x.md
|
||
scope: CameraX 采集
|
||
status: done
|
||
- id: T-AND-003
|
||
file: tasks/003-mediapipe-pose.md
|
||
scope: MediaPipe Pose 端侧推理
|
||
status: done
|
||
- id: T-AND-004
|
||
file: tasks/004-keypoint-serializer.md
|
||
scope: 33 点 -> 239 字节帧
|
||
status: done
|
||
- id: T-AND-005
|
||
file: tasks/005-websocket-client.md
|
||
scope: WebSocket 推流 + 自动重连
|
||
status: done
|
||
- id: T-AND-006
|
||
file: tasks/006-connect-ui.md
|
||
scope: 连接页 + 主机发现
|
||
status: done
|
||
- id: T-AND-007
|
||
file: tasks/007-running-ui.md
|
||
scope: 运行页 + 状态指示
|
||
status: done
|
||
- id: T-AND-008
|
||
file: tasks/008-about-page.md
|
||
scope: 关于页 + 开源声明
|
||
status: done
|
||
- id: T-AND-009
|
||
file: tasks/009-build-apk.md
|
||
scope: Android Studio 打包
|
||
status: done
|
||
- id: T-AND-010
|
||
file: tasks/010-about-version.md
|
||
scope: 「关于」清单展示当前版本号 + 契约版本单一来源
|
||
status: done
|
||
- id: T-AND-011
|
||
file: tasks/011-reconnect.md
|
||
scope: 连接状态机 / 保活 / 去重与 Windows 端对齐
|
||
status: done
|
||
- id: T-AND-012
|
||
file: tasks/012-mediapipe-crash.md
|
||
scope: MediaPipe 初始化崩溃修复 + 诊断
|
||
status: done
|
||
- id: T-AND-013
|
||
file: tasks/013-apk-version.md
|
||
scope: APK 产物版本元数据(AndroidManifest 的 versionName/versionCode)校验 + 打包脚本 die 未定义修复
|
||
status: done
|
||
- id: T-AND-014
|
||
file: tasks/014-release-acceptance.md
|
||
scope: 发布包级端到端验收(读 zip 内 APK:版本元数据 / ABI+MediaPipe native / 模型资源 / R8 / 契约快照)
|
||
status: done
|
||
- id: T-AND-015
|
||
file: tasks/015-native-libs.md
|
||
scope: APK native 库体检(ELF 类型 / DT_NEEDED 闭包 / 16 KB 页对齐),纯标准库实现
|
||
status: done
|
||
- id: T-AND-016
|
||
file: tasks/016-reconnect-integration.md
|
||
scope: JVM 内以真实 WebSocket(MockWebServer)验证重连链路 + 修「上一次关闭码污染下一次会话」
|
||
status: done
|
||
|
||
deliverable:
|
||
artifact: opendance-android-v0.2.1.apk
|
||
packaging: Gradle 命令行 / Android Studio
|
||
includes:
|
||
- app-release.apk
|
||
- THIRD_PARTY.md
|
||
- README.txt
|
||
- contracts/ # 契约快照
|
||
release_rules:
|
||
- release 包只含 arm64-v8a / armeabi-v7a(真机 ABI)
|
||
- 版本号单一来源:versionName(= project.yaml 的 version)与 BuildConfig 契约版本
|