课程介绍:协程在BLE开发中的角色与挑战
1.1 BLE开发的异步之痛
做BLE开发的朋友都懂——这玩意儿天生就是异步的。你发起一个连接,不知道什么时候回调;你发现服务,又得等;你读写特征值,还得等。每一步都是回调,嵌套起来那叫一个酸爽。
我几年前接手过一个BLE室内定位项目,代码里三层回调嵌套,连接成功回调里写发现服务,发现服务回调里写读写特征值。说实话,改一个逻辑要翻好几屏代码,心累。
更头疼的是生命周期。你的页面关掉了,BLE回调还在跑,它持有的那个Activity引用就成了泄漏点。你想想看,用户进进出出页面几次,内存就噌噌往上涨。
总结一下BLE开发的几个痛点:
- 回调地狱 —— 连接、发现、读写,每一步嵌套一步
- 生命周期脆弱 —— 页面销毁后回调仍在执行
- 错误处理分散 —— 每个回调都要单独处理异常
- 取消逻辑繁琐 —— 手动维护取消状态,容易遗漏
1.2 协程——异步救星,但...
协程的出现确实让BLE代码清爽了不少。用 suspend 函数把异步操作包起来,代码就变成了线性写法。说白了,就是用同步的写法搞定异步的逻辑。
你看这个对比:
| 维度 | 传统回调模式 | 协程模式 |
|---|---|---|
| 代码可读性 | 三层回调嵌套,维护成本高 | 线性书写,一目了然 |
| 生命周期管理 | 手动取消,靠自觉 | 作用域自动取消 |
| 错误处理 | 每个回调单独try-catch | 统一 try-catch,干净 |
| 泄漏风险 | 回调持有引用 | 协程不取消同样泄漏 |
看到最后一行了吗?嗯,这里要注意——协程并没有消灭泄漏,它只是换了一种泄漏的方式。我以前也觉得用了协程就万事大吉了,直到线上爆出一个内存泄漏,查了两天才发现是协程作用域没绑对。
为什么会这样?因为协程作用域如果不和组件的生命周期绑定,它就会一直活下去。你Activity都finish了,它还在后台等着BLE回调,那个Activity对象就永远回不了头。
1.3 泄漏的蝴蝶效应
咱们拆解一下泄漏是怎么发生的。其实链路很简单:
- 协程启动 —— 在Activity/Fragment里用
launch启动了协程 - 作用域未绑定 —— 没有用
viewModelScope或lifecycleScope,而是用全局作用域 - BLE操作阻塞 —— 协程在等待BLE回调,一直挂起
- 组件销毁 —— 用户退出页面,但协程没取消
- 引用无法释放 —— 协程持有外部的引用,GC回收不了
- 内存泄漏 —— 页面实例越来越多,OOM只是时间问题
我在项目中遇到过这样一个案例:一个蓝牙扫描页面,用户来回进出几次,内存就飙到了200多MB。用MAT一分析,发现每个Activity实例都被协程的Continuation对象强引用着。说白了,就是协程没取消,把整个页面都"拽"住了。
1.4 知识体系总览
这一章咱们要聊的东西,其实就围绕下面这张图展开。从左到右,从上到下,把"角色"和"挑战"讲透。
这张图就是我们这一章要走的路。从左边的"痛点"出发,到右边的"方案",再到中间的"风险"——说白了,就是先看清问题,再理解协程怎么帮我们,最后警惕它带来的新坑。下半部分就是实战环节了:怎么排查、怎么修复、怎么形成最佳实践。
1.5 学完本章你能带走什么
这一章不是干讲理论,我会带着你从真实案例出发,一步步拆解。你学完能带走这几样东西:
- 一套排查Ble泄漏的思维框架 —— 再遇到内存泄漏,知道从哪下手
- 几个实战工具的使用技巧 —— MAT、LeakCanary、Dump具体怎么看
- 协程作用域的正确绑定姿势 —— 什么场景用哪个Scope,门清
- 一份避坑清单 —— 我踩过的那些坑,你不用再踩一遍
🎯 一句话总结本章: 协程是BLE开发的好帮手,但作用域管理搞不好,它会变成泄漏的源头。咱们这一章,就是把它的脾气摸透,让它乖乖听话。