第2章 BLE通信基础:GATT协议、Service/Characteristic/Descriptor、MTU与连接参数

2.1 GATT协议概述

GATT(Generic Attribute Profile)是BLE(Bluetooth Low Energy)应用层通信的核心协议,定义了设备之间如何通过Attribute Protocol(ATT)传输数据。GATT采用Client/Server模型:

  • GATT Client(通常为手机、平板)发起请求,读写数据。
  • GATT Server(通常为外设、传感器)存储数据,响应请求。

GATT 建立在 GAP(Generic Access Profile)之上——GAP 负责发现与连接,GATT 负责连接后的数据交互。两者分工明确:GAP 管“连接”,GATT 管“通信”。

要点:在依赖注入框架集成BLE组件时,GATT 的 Client/Server 关系决定了 Service 层的接口设计——通常将 GATT Client 抽象为可注入的 BLE 连接管理器。

2.2 GATT 三层结构:Service / Characteristic / Descriptor

GATT 数据以层级结构组织,自上而下依次为 Profile → Service → Characteristic → Descriptor,每个元素通过 16-bit 或 128-bit UUID 唯一标识。

层级说明UUID 示例包含关系
Service(服务)一组相关功能的集合,如“电量服务”0x180F包含 1~N 个 Characteristic
Characteristic(特征)一个具体数据点,如“电量百分比”0x2A19包含 Value + 0~N 个 Descriptor
Descriptor(描述符)描述 Characteristic 的属性或配置,如 CCCD 用于启用通知0x2902属于一个 Characteristic
  • Service:逻辑分组,例如设备信息服务(DIS, 0x180A)包含设备名、序列号等特征。
  • Characteristic:实际读写的数据单元,包含 Value 和 Properties(读、写、通知、指示)。
  • Descriptor:附加元数据,最常用的是 CCCD(Client Characteristic Configuration Descriptor,0x2902),用于启停通知/指示。
设计习惯:在依赖注入框架中,每个 Service 通常对应一个 @Singleton 的 Repository 或 Service 类,Characteristic 的读写方法则封装为可注入的 UseCase。

2.3 MTU 与连接参数

MTU(Maximum Transmission Unit)定义了 GATT 单次数据包的最大 payload 字节数。BLE 4.x 默认 MTU 为 23 字节(含 3 字节 ATT 头部,实际有效负载 20 字节),BLE 5.x 可协商至 512 字节。

连接参数决定了 BLE 链路的功耗与实时性,由连接间隔、从机延迟、超时时间三个关键值构成:

参数含义典型范围对性能的影响
Connection Interval主机与从机之间的数据交换周期7.5 ms ~ 4 s间隔越小,数据延迟越低,功耗越高
Slave Latency从机可跳过的最大连接事件次数0 ~ 4 次延迟越大,从机休眠时间越长,功耗越低
Supervision Timeout连接丢失的超时阈值10 ms ~ 32 s超时过短易误判断开,过长则响应迟钝
MTU单个 ATT PDU 的最大字节数23 ~ 512 bytesMTU 越大,大包传输越快,但需双方协商支持
常见误区:MTU 并非越大越好——超过双方硬件 FIFO 容量会导致分片重传,反而降低吞吐。建议在连接后通过 requestMtu() 协商到双方都支持的最大值。
GATT 协议层次结构与通信参数 GATT 协议层次结构与通信参数 GATT Profile(应用规范) Service A(0x180F) Service B(0x180A) Characteristic A1 Characteristic A2 Characteristic B1 Descriptor CCCD Descriptor Value Descriptor CCCD Descriptor Value MTU: 23~512 B 连接间隔: 7.5ms~4s 从机延迟: 0~4 次 超时时间: 10~32 s 连接参数 分层关系:Profile < Service < Characteristic < Descriptor GATT Client(手机)通过读写 Characteristic 与 GATT Server(外设)交互,CCCD 用于启用通知/指示

2.4 GATT 通信流程

一次完整的 BLE GATT 交互遵循以下步骤:

  1. 扫描与连接(GAP 层):发现外设并发起连接。
  2. 服务发现:Client 调用 discoverServices(),Server 返回所有 Service 及下属 Characteristic、Descriptor。
  3. MTU 协商:Client 发起 requestMtu(),双方确认最大传输单元。
  4. 读写操作:根据业务需求读取 Characteristic Value 或写入数据。
  5. 通知/指示订阅:写入 CCCD 开启通知通道,Server 主动推送数据。
  6. 断开与清理:释放 Gatt 资源,断开连接。
依赖注入关注点:将上述流程中的“服务发现”和“MTU 协商”封装为 Observable/Flow 异步操作,注入到 ViewModel 或 UseCase 层,可使 BLE 逻辑与 UI 完全解耦。

2.5 代码示例:GATT 服务发现与特征读写

以下示例展示了 Android(Java)和 iOS(Swift)两端执行 GATT 服务发现与特征读取的核心代码。

Android(使用 BluetoothGatt)

BluetoothGatt gatt = device.connectGatt(context, false, new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            // 连接成功,开始服务发现
            gatt.discoverServices();
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            // 处理断开
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            for (BluetoothGattService service : gatt.getServices()) {
                // 根据 UUID 找到目标 Service
                if (service.getUuid().toString().equals(DEVICE_INFO_SERVICE_UUID)) {
                    for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
                        // 找到目标 Characteristic 后读取
                        if (characteristic.getUuid().toString().equals(BATTERY_LEVEL_UUID)) {
                            gatt.readCharacteristic(characteristic);
                        }
                    }
                }
            }
        }
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic,
                                     int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            byte[] value = characteristic.getValue();
            // 处理读取到的数据
        }
    }

    @Override
    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            // MTU 协商成功,当前 mtu 为协商值
        }
    }
});

// 发起 MTU 协商(在连接成功后调用)
gatt.requestMtu(256);

iOS(使用 CoreBluetooth)

class BLEManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    var centralManager: CBCentralManager!
    var peripheral: CBPeripheral?
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            central.scanForPeripherals(withServices: [DIS_SERVICE_UUID], options: nil)
        }
    }
    func centralManager(_ central: CBCentralManager,
                        did