蓝牙 GATT(Generic Attribute Profile)
GATT(通用属性协议)是低功耗蓝牙(BLE)的核心协议之一,定义了设备之间如何通过属性(Attribute) 进行数据交换。它是构建 BLE 应用的基础框架,适用于设备间传输小规模、低频次数据(如传感器数据、指令控制等)。
一、GATT 核心概念与角色
1、角色划分
- GATT Server(服务端):存储数据并提供服务(如心率传感器存储心率数据)。
- GATT Client(客户端):主动发起请求,读取或修改服务端数据(如手机 App 读取传感器数据)。 注:一个设备可以同时作为 Server 和 Client(如智能手表既向手机发送数据,又控制耳机)。
2、协议栈位置 GATT 基于ATT(Attribute Protocol)协议实现,属于 BLE 协议栈的上层应用层:
┌───────────────┐
│ GATT │
├───────────────┤
│ ATT │
├───────────────┤
│ L2CAP │
├───────────────┤
│ HCI │
└───────────────┘
二、GATT 数据模型:服务、特征与描述符
GATT 数据以层级结构 组织,包含三个核心层级:
Service(服务)
- 作用:封装一组功能相关的操作(如电池服务、心率服务)。
- UUID:唯一标识符(16-bit 或 128-bit)。
- 标准服务 UUID(如 0x180D 代表心率服务)。
- 自定义服务 UUID(开发者定义,格式如 0000XXXX-0000-1000-8000-00805F9B34FB)。
Characteristic(特征)
- 作用:服务中的具体数据点(如心率值、电池电量)。
- 属性:
- PROPERTY_READ:可读
- PROPERTY_WRITE:可写
- PROPERTY_NOTIFY:可通知(服务端主动推送数据)
- PROPERTY_INDICATE:需确认的通知(客户端需回复确认)
- 结构:
┌───────────────────┐
│ Characteristic │
│ - Value │ // 实际数据(如 0x64 表示心率 100)
│ - Descriptors │ // 描述符(如客户端配置通知)
└───────────────────┘
Descriptor(描述符)
- 作用:描述特征的元数据或配置选项。
- 常用描述符:
- Client Characteristic Configuration Descriptor(CCCD,UUID: 0x2902)用于启用/禁用特征的通知(NOTIFY 或 INDICATE)。写入 0x0001 启用通知,0x0000 禁用。
- Characteristic User Description(UUID: 0x2901)存储人类可读的特征描述(如“心率测量值”)。
三、GATT 数据操作流程(Android 示例)
1. 客户端连接与发现服务
// 连接 GATT 服务端(以手机作为客户端连接传感器为例)
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(); // 发现服务
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 获取所有服务
List<BluetoothGattService> services = gatt.getServices();
// 查找目标服务(如心率服务)
BluetoothGattService heartRateService = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"));
}
}
});
2. 读取特征值
BluetoothGattCharacteristic characteristic = heartRateService.getCharacteristic(UUID.fromString("00002A37-0000-1000-8000-00805F9B34FB"));
gatt.readCharacteristic(characteristic); // 触发读取请求
// 在回调中接收数据
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
byte[] data = characteristic.getValue(); // 解析数据(如心率值)
}
}
3. 启用通知(监听服务端推送)
// 启用通知
gatt.setCharacteristicNotification(characteristic, true);
// 配置 gatt 描述符
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805F9B34FB"));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
// 接收通知数据
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
byte[] data = characteristic.getValue(); // 实时数据更新
}
4. 写入特征值(向服务端发送指令)
characteristic.setValue(new byte[]{0x01}); // 设置写入值(如开启传感器)
gatt.writeCharacteristic(characteristic);
// 写入结果回调
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d("GATT", "写入成功");
}
}
四、GATT 开发注意事项
连接与线程安全
- BLE 操作(如 connectGatt、readCharacteristic)必须在主线程调用,但回调可能在其他线程触发,需使用 Handler 或 runOnUiThread 更新 UI。
- 连接超时处理:Android 默认无连接超时机制,需手动实现(如 Handler.postDelayed)。
MTU 与数据传输优化
- MTU(Maximum Transmission Unit):默认 23 字节,可通过 gatt.requestMtu() 协商更大的值(如 512 字节),提升传输效率。
- 数据分包:长数据需分多次传输,并处理顺序和完整性。
权限与兼容性
- Android 12+:需动态申请 BLUETOOTH_CONNECT、BLUETOOTH_SCAN 权限。
- 设备差异:部分设备对通知(NOTIFY)支持不稳定,需测试重连机制。
资源释放
断开连接后必须调用 gatt.close(),否则会导致连接泄漏。
五、常见问题与解决
-
连接失败(status ≠ GATT_SUCCESS)
- 检查设备是否在广播,或尝试重启蓝牙适配器。
- 确保应用有正确的定位权限(Android 6.0+ 需要 ACCESS_FINE_LOCATION 用于 BLE 扫描)。
-
特征读写返回 GATT_READ_NOT_PERMITTED
- 服务端特征未配置正确的权限(如客户端尝试写入只读特征)。
-
通知不触发
- 确认已调用 setCharacteristicNotification(true) 并正确写入 CCCD 描述符。
- 服务端特征需配置 PROPERTY_NOTIFY 或 PROPERTY_INDICATE。
-
跨设备兼容性问题
- 不同厂商对 GATT 规范实现可能不一致,需实测并处理异常(如重试机制)。
通过理解 GATT 的结构与操作流程,结合 Android API 的实现,可以高效开发 BLE 应用(如健康监测、智能家居控制等)。