fix(cw): stop the waterfall crashing and cut APK size by 87MB
三个真机实测暴露的问题:
1. 闪退 (给权限后 3-4 秒必崩, 且注入日志抓不到堆栈)
CwWaterfallState 用 mutableIntStateOf 记录重绘版本号, 却从音频采集线程
写入。Compose 快照状态只能在合成线程修改, 从后台线程写会在运行时崩溃 ——
崩在 Compose 内部, 所以业务类的日志注入抓不到。
改用 MutableStateFlow (本身线程安全), Canvas 侧 collectAsState 读取。
2. 同一状态的跨线程数据竞争
pushSamples 在采集线程写 rows/pending, snapshot() 在绘制线程读, 而
ArrayDeque 非线程安全 —— 并发 removeFirst()/toList() 会抛
ConcurrentModificationException 或 IndexOutOfBoundsException。
两侧统一加锁; FFT 计算放在锁外, 只有队列追加持锁。
3. APK 从 8.4MB 暴涨到 135MB
onnxruntime-android 的 AAR 自带 4 个架构原生库: arm64 28M + armv7 20M +
x86 33M + x86_64 34M = 115MB。上次提交移除 abiFilters 时把 x86 系列也
打包了进去 (仅模拟器需要)。
重新加上 abiFilters, 保留 arm64-v8a + armeabi-v7a 两个真机 ABI,
预计降至约 43MB。注意这与上次"恢复 64 位"不冲突: 那次删的是只留
armeabi-v7a 的限制, 这次是排除 x86 系列。
另外两处加固:
- CwDeepDecoder 的模型加载从 init{} 移入惰性 ensureLoaded(): 加载会触发
ONNX Runtime 原生库装载, 失败时抛 UnsatisfiedLinkError; 在构造函数中抛出
会连带崩掉创建它的 composable, try-catch 也救不回来。移到首次使用时执行,
失败经 errorMessage 上报给 UI。
- ONNX 会话限制 intraOp 线程数为 (核数-1) 且上限 4, 给音频采集和 UI 留出
余量, 默认行为会铺满所有核心。
验证:
./gradlew :feature:cw:compileDebugKotlin :core:data:compileDebugKotlin => 通过
./gradlew :core:domain:test => 79 个测试全绿