GATT(Generic Attribute Profile)— 蓝牙低功耗(BLE)协议,定义了数据结构以及 BLE 设备之间的信息交换规则。GATT 构建在属性协议(ATT)之上,并将数据组织成层次结构:服务(services)、特征(characteristics)和描述符(descriptors)。根据蓝牙 SIG(2025)的数据,GATT 协议被用于 98% 的 BLE 应用程序中 — 从健身追踪器到智能锁和医疗传感器。
要点
GATT(Generic Attribute Profile)— 是蓝牙低功耗(BLE)协议,定义了两个 BLE 设备如何通过属性协议(ATT)交换数据。GATT 标准化了数据结构:所有数据被组织为“服务 → 特征 → 描述符”的层次结构。该协议在蓝牙 4.0 规范(2010)中与 BLE 一起引入,此后一直是节能蓝牙设备的主要数据传输机制。与经典蓝牙通过串口(SPP)传输数据不同,GATT 通过读取、写入和通知操作提供结构化的数据访问。
BLE 协议栈由多个层组成:物理层(无线电)、链路层(连接管理)、L2CAP(逻辑信道传输)、ATT(属性协议 — 属性访问)和 GATT(基于 ATT 的协议)。GATT 是应用程序开发人员处理的最高层。底层的 ATT 提供基本操作:读取、写入、通知和指示属性。GATT 则添加了语义:定义什么是服务、什么是特征、它们如何分组以及读写时适用哪些规则。根据 蓝牙核心规范 5.4(2023),GATT 在单个设备上支持多达 65,535 个属性(服务 + 特征 + 描述符)。
GATT 层次结构由三个级别组成。服务(Service)— 解决一个任务的特征的逻辑组(例如,“电池服务”或“心率服务”)。特征(Characteristic)— 具有已知类型的数据单元:当前电量、传感器读数、开关状态。每个特征都有一个值(value)和一个或多个描述符(Descriptors),描述符描述元数据:测量单位、通知设置、值范围。UUID(通用唯一标识符)唯一标识每个服务和每个特征。
GATT 支持四种与特征交互的操作类型。读取(Read)— 客户端从服务器请求特征的当前值。写入(Write)— 客户端向服务器发送新值。通知(Notify)— 服务器向客户端发送值而无需确认(更快但可靠性较低)。指示(Indicate)— 服务器发送值并确认接收(更可靠但较慢)。开发人员根据场景选择类型:对于脉搏传感器读数,Notify 足够了;对于写入智能锁配置,则需要带确认的 Write。
// Android 代码中的 GATT 数据结构
data class BleService(
val uuid: UUID,
val characteristics: List<BleCharacteristic>
)
data class BleCharacteristic(
val uuid: UUID,
val properties: Int, // 读取、写入、通知、指示
val descriptors: List<BleDescriptor>,
var value: ByteArray?
)
在 BLE 规范中定义了两个主要协议:GAP(通用访问协议)和 GATT(通用属性协议)。GAP 负责设备发现、连接建立和可见性管理 — 这是 BLE 的“网络层”。GATT — 负责连接后的数据交换,即“应用层”。开发人员使用 GAP 扫描和连接设备,使用 GATT 从连接的设备读取、写入和接收通知。
| 特性 | GAP | GATT |
|---|---|---|
| 目的 | 发现和连接 | 数据交换 |
| 角色 | 中央/外围 | 客户端/服务器 |
| 协议 | HCI、链路层 | ATT(属性协议) |
| 阶段 | 连接之前 | 连接之后 |
| 主要类 | BluetoothAdapter | BluetoothGatt |
GATT 定义了两个角色:GATT 服务器和 GATT 客户端。服务器 — 提供数据的设备(例如,传输脉搏读数的健身追踪器)。客户端 — 请求数据的设备(例如,读取测量值的智能手机)。在大多数场景中,Android 应用程序充当 GATT 客户端,BLE 外设充当 GATT 服务器。然而,Android 也可以充当 GATT 服务器 — 例如,如果应用程序为其他设备模拟 BLE 设备。角色在 GATT 连接建立阶段确定,并且在会话期间不会改变。
GATT 通信过程始于设备之间建立 BLE 连接之后。首先,GATT 客户端通过服务发现(Service Discovery)操作检测 GATT 服务器上的服务 — Android 在连接时通过 BluetoothGatt.discoverServices() 自动执行此操作。发现后,客户端获取可用服务、特征和描述符的列表。然后客户端可以读取特征值(Read)、写入新值(Write)或订阅通知(Set Notify/Indicate)。GATT 服务器在建立连接后可以随时向客户端发送 Notify/Indicate。
默认情况下,BLE 中单个 GATT 数据包的大小(MTU,最大传输单元)为 23 字节,其中 3 字节为 ATT 标头,20 字节为有效数据。如果应用程序需要传输更多数据(例如 OTA 固件更新),可以通过 Android 中的 requestMtu() 请求将 MTU 增加到 517 字节。增加的 MTU 减少了传输一个数据块所需的包数量 — 可以发送 2 个大包而不是 50 个小包,从而降低功耗并加快传输速度。最大 MTU 取决于 BLE 版本和芯片能力 — 蓝牙 5.0 支持最多 517 字节,较旧版本支持最多 247 字节。
// 订阅特征通知
fun enableNotifications(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
gatt.setCharacteristicNotification(characteristic, true)
// 启用 CCCD(客户端特征配置描述符)
val cccdUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val descriptor = characteristic.getDescriptor(cccdUuid)
descriptor?.let {
it.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
gatt.writeDescriptor(it)
}
}
要在 Android 中从 GATT 服务器接收通知,需要实现 BluetoothGattCallback.onCharacteristicChanged() 回调。在此方法中,应用程序在服务器每次发送 Notify 或 Indicate 时接收更新的特征值。对于实时数据处理,请使用缓冲区和 Kotlin 协程 — 这样可以防止在密集通知(例如每秒 100+ 次脉搏读数)时阻塞 UI 线程。对于指示(Indicate),务必在服务器端调用 gatt.sendResponse() — 在 Android 客户端中,这由系统自动处理。
在 Android 中,GATT 的开发通过 android.bluetooth 包中的类实现:BluetoothGatt(连接)、BluetoothGattService(服务)、BluetoothGattCharacteristic(特征)和 BluetoothGattDescriptor(描述符)。连接到 BLE 设备始于调用 BluetoothDevice.connectGatt() — 该方法返回 BluetoothGatt,通过它执行所有后续操作。所有 GATT 回调都通过 BluetoothGattCallback 接收 — 这是一个异步接口,在创建 BluetoothGatt 的同一线程中调用。重要提示:所有 GATT 操作必须按顺序执行 — 在同一 BluetoothGatt 上同时调用多个操作会导致错误。
正确管理 GATT 连接的生命周期对于 BLE 应用程序的稳定性至关重要。调用 connectGatt() 后,应用程序等待状态为 STATE_CONNECTED 的 onConnectionStateChange() 回调。然后系统自动启动服务发现,完成后调用 onServicesDiscovered()。只有在此之后才能执行读取、写入和订阅。工作完成后,务必在 onDestroy() 或 onPause() 中调用 gatt.close() — 未关闭的 GATT 连接会消耗电池,并可能阻止在 Android 上重新连接到同一设备。
// 通过 GATT 连接到 BLE 设备
private val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(
gatt: BluetoothGatt, status: Int, newState: Int
) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices()
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
val service = gatt.getService(UUID.fromString("180D"))
val characteristic = service?.getCharacteristic(
UUID.fromString("2A37")
)
characteristic?.let { enableNotifications(gatt, it) }
}
}
蓝牙 SIG(特别兴趣小组)定义了数十种具有固定 16 位 UUID 的标准 GATT 协议和服务。最常见的有:电池服务(Battery Service,UUID 180F,特征 Battery Level 2A19)、心率服务(Heart Rate,UUID 180D,特征 Heart Rate Measurement 2A37)、设备信息服务(Device Information,UUID 180A,特征 Manufacturer Name、Serial Number)。使用标准服务可确保不同制造商设备之间的兼容性 — 任何具有心率服务的健身手环都应能与任何支持它的 Android 应用程序配合使用。
如果标准服务无法满足任务需求,开发人员可以创建自己的 GATT 服务,使用 128 位 UUID(自定义 UUID)。在设计自定义服务时,需要:定义特征的逻辑组(例如,“锁管理服务”),为每个特征分配正确的属性(Read、Write、Notify),通过特征表示格式描述符(Characteristic Presentation Format)确定允许的值和测量单位。对于复杂协议,建议在服务中包含具有 Write 属性的 Command 特征和具有 Notify 属性的 Status 特征 — 这符合工业 BLE 应用程序中采用的 Command/Status 模式。
// 创建自定义 GATT 服务(Android 作为 GATT 服务器)
private fun createCustomService(): BluetoothGattService {
val serviceUuid = UUID.fromString("12345678-1234-5678-1234-56789abcdef0")
val service = BluetoothGattService(
serviceUuid, BluetoothGattService.SERVICE_TYPE_PRIMARY
)
val charUuid = UUID.fromString("12345678-1234-5678-1234-56789abcdef1")
val characteristic = BluetoothGattCharacteristic(
charUuid,
BluetoothGattCharacteristic.PROPERTY_READ or
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ
)
service.addCharacteristic(characteristic)
return service
}
常见问题
GATT(通用属性协议)— 是蓝牙低功耗协议,定义了设备之间的数据交换结构。它用于标准化信息访问:所有 BLE 设备将数据组织成服务和特征,允许任何客户端读取传感器读数、控制设备和接收通知。
GAP 负责 BLE 设备的发现和连接(扫描、广播、建立连接)。GATT — 负责连接后的数据交换(读取、写入、通知)。GAP 在连接前工作,GATT 在连接后工作。两个协议都是 BLE 必需的,但功能不同:GAP 是“网络层”,GATT 是“应用层”。
标准服务包括电池服务(Battery Service,180F,电量)、心率服务(Heart Rate,180D,脉搏)、设备信息服务(Device Information,180A,设备数据)。每个服务包含多个具有 16 位 UUID 的特征。开发人员可以使用 128 位 UUID 创建自定义服务用于特定任务 — 例如控制智能锁或传输健身追踪器数据。
在 Android 上,GATT 通过 BluetoothGatt(连接)、BluetoothGattService(服务)、BluetoothGattCharacteristic(特征)和 BluetoothGattDescriptor(描述符)实现。连接通过 connectGatt() 完成,之后事件通过 BluetoothGattCallback 回调接收:onConnectionStateChange、onServicesDiscovered、onCharacteristicChanged(用于通知)。所有 GATT 操作必须按顺序执行。
UUID(通用唯一标识符)— 16 位或 128 位的标识符,唯一确定 GATT 中的服务或特征。标准蓝牙 SIG 服务使用 16 位 UUID(例如 180D 对应心率服务)。开发人员的自定义服务使用 128 位 UUID(例如 12345678-1234-5678-1234-56789abcdef0)。UUID 使客户端能够在 GATT 服务器上找到所需的数据。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。