1. GATT协议基础
做BLE开发这么多年,我遇到过不少回调里协程泄漏的案子。要搞清楚这个问题,得先把GATT协议的老底摸清楚。你想想看,连Service和Characteristic都分不清,怎么去排查回调里的协程泄漏?
我个人习惯是,学任何协议都先看它的层级结构——这就像盖楼,得先知道地基在哪、哪层是承重墙。GATT也是这样,它并不是孤立存在的,而是整个BLE协议栈里最上面那层。
1.1 BLE协议栈架构
BLE协议栈从下往上分了好几层,每层各司其职。我刚开始做蓝牙开发时,总觉得分层是理论派的东西,直到有一次设备老是断连,我花了三天定位,最后发现是链路层的连接参数没配好——从那以后,我再也不敢轻视每一层了。
从底往上,最核心的几层是这样的:
- 物理层(Physical Layer)——负责2.4GHz频段上的无线收发。说白了就是管天线的活,调制解调、跳频都在这一层。
- 链路层(Link Layer)——管理连接状态、广播、扫描、连接参数。你平时调的那个连接间隔,就在这里控制。
- HCI(Host Controller Interface)——芯片内部主机和控制器之间的通道。嗯,大部分应用开发者不太直接碰它,但底层驱动少不了。
- L2CAP——负责数据的封装和分段重组。它给上层提供了逻辑通道,ATT的数据就是通过它传的。
- ATT(Attribute Protocol)——属性协议,定义了如何读写属性数据。GATT就是基于ATT的。
- GATT(Generic Attribute Profile)——咱们这节课的主角,定义了属性数据组织成Service、Characteristic的规范。
这里要注意,GATT和ATT经常被混着说,其实ATT是传输协议,GATT是数据组织规范。我在项目评审时发现不少人搞混它俩,结果代码里把ATT的回调当GATT用,调试时一头雾水。
1.2 GATT角色与Profile
GATT通信中有两个核心角色:Server和Client。
- GATT Server——持有数据的一方,提供Service/Characteristic供对方读写。通常传感器设备作为Server。
- GATT Client——发起读写请求的一方,从Server获取数据。手机App一般是Client。
我在项目中遇到过这样一个场景:一个设备同时需要提供数据又需要读取另一个设备的数据,那它就同时充当Server和Client。这种"双重身份"在复杂拓扑里很常见,不过也容易搞混回调的归属——嗯,这也是协程泄漏的潜在温床。
再说说Profile(配置文件)。Profile不是代码里的实体,而是一份规范文档,定义了设备应该包含哪些Service,以及它们的行为。比如心率Profile规定必须有心率测量Service和设备信息服务。说白了,Profile就是"设备功能说明书"。
1.3 Service/Characteristic/Descriptor结构
这是GATT最核心的数据模型,也是排查回调协程泄漏时最需要熟悉的结构。我把它叫做"三层汉堡":
- Service(服务)——一组相关功能的集合。比如电池服务包含电量百分比和电量状态两个数据点。
- Characteristic(特征)——单个数据点,是读写操作的最小单元。每个Characteristic有一个值(Value)和若干属性(Properties),比如可读、可写、可通知。
- Descriptor(描述符)——描述Characteristic的额外信息。最常见的CCCD(Client Characteristic Configuration Descriptor)用来开启通知或指示。
这三者的包含关系:一个Profile包含多个Service,一个Service包含多个Characteristic,一个Characteristic可以包含多个Descriptor。
我曾经在一个项目中,把所有数据都塞进一个Service里,结果Characteristic数量炸了,管理起来痛苦不堪。后来才意识到,合理的Service划分——比如按功能模块拆——对代码可维护性有多重要。那次重构之后,回调逻辑也清晰多了。
| 层级 | 作用 | 包含关系 | 示例 |
|---|---|---|---|
| Service | 功能集合,按模块组织数据 | 包含多个Characteristic | 电池服务 (0x180F) |
| Characteristic | 单个数据点,可读/写/通知 | 从属于某个Service | 电池电量 (0x2A19) |
| Descriptor | 描述特征的配置或元数据 | 从属于某个Characteristic | CCCD (0x2902) |
讲到这里,GATT的基础框架应该已经清楚了。这一层一层的结构,说白了就是为了让数据能规规矩矩地在设备和手机之间流转。后面咱们讨论回调里的协程泄漏,你得先能在脑子里画出这张"层级地图",才能快速定位问题出在哪一层、哪个节点上。