做设备采集类的应用,最怕用户反馈里的一句话:「蓝牙断了就连不上,只能杀掉应用重开。」这篇记录从这行反馈开始的三个月治理——两本账:连接的账和内存的账。

一、连接的账:重连失败的根因是时序

先说结论:断连回调里直接 connect() 是错误做法。此时链路状态机可能还没走完断开流程,立即重连会被底层拒绝且不进队列——连击式重连正是「必须杀应用」的头号来源。

正确的时序分四步:收到 STATE_DISCONNECTED 先区分主动断开还是异常断开;异常断开用同一个 gattClient 对象直接 connect(复用不重建,这是官方推荐路径);确需重建时,先 disconnect 再 close 旧实例再 new 新的;重连加退避——1s、2s、4s,连续五次失败就停,等设备端广播恢复再触发。退避不是为了礼貌,是给状态机留出收敛的时间。

二、连接参数的两个认知纠偏

supervisionTimeout 的单位是 10ms 不是 1.25ms(connInterval 才是 1.25ms)。表里写 3000 对应 30 秒——链路闲置 30 秒才判死,这个值本身合理,但 App 侧判断「链路多久会被杀」、设计重连节奏时,这个换算必须先搞对。

更要紧的边界:ArkTS 侧 @ohos.ble 没有暴露连接间隔和 supervisionTimeout 的设置接口——这些参数的正确用途是拿去审外设固件的连接参数表,而不是在 App 里调。App 侧真正能做的是三件事:数据走 notify 而不是定时轮询、断连后按上面的时序重连、切后台要持续收数据就申请 BLUETOOTH_INTERACTION 长时任务——后台被杀是系统策略不是 bug,长时任务才是正解。

三、内存的账:炸的位置在解码不在识别

设备传回的照片要识别和压缩,用户报「处理几张图内存就飙」。病根:相机原图 4000×3000 解码成 RGBA_8888 一张就是约 48MB——识别引擎还没跑,解码这步就可能触发内存告警。

核心一招:解码前先算采样率。用 image.createImageSource 的 desiredSize 按展示/识别需要的尺寸一次解码到位——文字识别长边 2000 像素足够,内存直接从 48MB 降到 12MB 一档。预览图长边 1000 像素足够,再降一个量级。

四、处理流水线的三条纪律

处理放 TaskPool 且串行不并发——BLE 收数据本来就有节奏,多张图排队处理没必要抢内存;压缩用 image.PackingConfig 转 JPEG(quality 80 起步),处理完的中间 PixelMap 立即 release;设备分片传图的场景,收完一个完整文件再解码——边收边解会把收数据的内存峰值和解码的内存峰值叠在一起,翻车概率翻倍。

五、验证方法

连接侧:手动开关蓝牙、走出覆盖范围再回来、设备端重启,三种断连场景各测二十次,记录重连成功率——退避调参前后对比,成功率从六成到九成五的变化就是答案。内存侧:Profiler 的 Memory 面板看连续处理十张图的内存曲线,应该是锯齿状回落而不是阶梯状爬升——阶梯说明有中间产物没释放。

收束

BLE 的账在「状态机时序」——断开后等回调走完再重连、退避给收敛时间;内存的账在「解码尺寸与释放时机」——采样解码、TaskPool 串行、即用即释放。两本账都算清之后,那条「必须杀应用」的反馈再没出现过。设备类应用的稳定性没有玄学,只有一笔一笔算清的账。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐