GATT — 是什么,Generic Attribute Profile 及在 Android 中的实现

作者: IT Sectr 发布日期: 2026-07-14 阅读时间: 10 分钟

GATT(Generic Attribute Profile)— 蓝牙低功耗(BLE)协议,定义了数据结构以及 BLE 设备之间的信息交换规则。GATT 构建在属性协议(ATT)之上,并将数据组织成层次结构:服务(services)、特征(characteristics)和描述符(descriptors)。根据蓝牙 SIG(2025)的数据,GATT 协议被用于 98% 的 BLE 应用程序中 — 从健身追踪器到智能锁和医疗传感器。

要点

  • GATT — BLE 协议,定义了通过服务、特征和描述符传输数据的结构
  • 层次结构 — 服务包含特征,每个特征是一个值,带有可选的描述符
  • UUID — 每个服务和特征由 16 位(标准)或 128 位(自定义)UUID 标识
  • 角色 — GATT 服务器(提供数据)和 GATT 客户端(读取/写入数据)
  • Android API — BluetoothGatt、BluetoothGattService、BluetoothGattCharacteristic — 用于 GATT 开发的主要类

什么是 GATT?

GATT(Generic Attribute Profile)— 是蓝牙低功耗(BLE)协议,定义了两个 BLE 设备如何通过属性协议(ATT)交换数据。GATT 标准化了数据结构:所有数据被组织为“服务 → 特征 → 描述符”的层次结构。该协议在蓝牙 4.0 规范(2010)中与 BLE 一起引入,此后一直是节能蓝牙设备的主要数据传输机制。与经典蓝牙通过串口(SPP)传输数据不同,GATT 通过读取、写入和通知操作提供结构化的数据访问。

GATT 在 BLE 协议栈中的角色

BLE 协议栈由多个层组成:物理层(无线电)、链路层(连接管理)、L2CAP(逻辑信道传输)、ATT(属性协议 — 属性访问)和 GATT(基于 ATT 的协议)。GATT 是应用程序开发人员处理的最高层。底层的 ATT 提供基本操作:读取、写入、通知和指示属性。GATT 则添加了语义:定义什么是服务、什么是特征、它们如何分组以及读写时适用哪些规则。根据 蓝牙核心规范 5.4(2023),GATT 在单个设备上支持多达 65,535 个属性(服务 + 特征 + 描述符)。

GATT 架构:服务、特征和描述符

GATT 层次结构由三个级别组成。服务(Service)— 解决一个任务的特征的逻辑组(例如,“电池服务”或“心率服务”)。特征(Characteristic)— 具有已知类型的数据单元:当前电量、传感器读数、开关状态。每个特征都有一个值(value)和一个或多个描述符(Descriptors),描述符描述元数据:测量单位、通知设置、值范围。UUID(通用唯一标识符)唯一标识每个服务和每个特征。

特征操作类型

GATT 支持四种与特征交互的操作类型。读取(Read)— 客户端从服务器请求特征的当前值。写入(Write)— 客户端向服务器发送新值。通知(Notify)— 服务器向客户端发送值而无需确认(更快但可靠性较低)。指示(Indicate)— 服务器发送值并确认接收(更可靠但较慢)。开发人员根据场景选择类型:对于脉搏传感器读数,Notify 足够了;对于写入智能锁配置,则需要带确认的 Write。

kotlin
// 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?
)

GATT 和 GAP:BLE 协议的区别

BLE 规范中定义了两个主要协议:GAP(通用访问协议)和 GATT(通用属性协议)。GAP 负责设备发现、连接建立和可见性管理 — 这是 BLE 的“网络层”。GATT — 负责连接后的数据交换,即“应用层”。开发人员使用 GAP 扫描和连接设备,使用 GATT 从连接的设备读取、写入和接收通知。

特性GAPGATT
目的发现和连接数据交换
角色中央/外围客户端/服务器
协议HCI、链路层ATT(属性协议)
阶段连接之前连接之后
主要类BluetoothAdapterBluetoothGatt

GATT 中的角色

GATT 定义了两个角色:GATT 服务器GATT 客户端。服务器 — 提供数据的设备(例如,传输脉搏读数的健身追踪器)。客户端 — 请求数据的设备(例如,读取测量值的智能手机)。在大多数场景中,Android 应用程序充当 GATT 客户端,BLE 外设充当 GATT 服务器。然而,Android 也可以充当 GATT 服务器 — 例如,如果应用程序为其他设备模拟 BLE 设备。角色在 GATT 连接建立阶段确定,并且在会话期间不会改变。

GATT 通信在 BLE 中如何工作

GATT 通信过程始于设备之间建立 BLE 连接之后。首先,GATT 客户端通过服务发现(Service Discovery)操作检测 GATT 服务器上的服务 — Android 在连接时通过 BluetoothGatt.discoverServices() 自动执行此操作。发现后,客户端获取可用服务、特征和描述符的列表。然后客户端可以读取特征值(Read)、写入新值(Write)或订阅通知(Set Notify/Indicate)。GATT 服务器在建立连接后可以随时向客户端发送 Notify/Indicate。

MTU — 最大数据包大小

默认情况下,BLE 中单个 GATT 数据包的大小(MTU,最大传输单元)为 23 字节,其中 3 字节为 ATT 标头,20 字节为有效数据。如果应用程序需要传输更多数据(例如 OTA 固件更新),可以通过 Android 中的 requestMtu() 请求将 MTU 增加到 517 字节。增加的 MTU 减少了传输一个数据块所需的包数量 — 可以发送 2 个大包而不是 50 个小包,从而降低功耗并加快传输速度。最大 MTU 取决于 BLE 版本和芯片能力 — 蓝牙 5.0 支持最多 517 字节,较旧版本支持最多 247 字节。

kotlin
// 订阅特征通知
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 客户端中,这由系统自动处理。

GATT 在 Android 中的实现

在 Android 中,GATT 的开发通过 android.bluetooth 包中的类实现:BluetoothGatt(连接)、BluetoothGattService(服务)、BluetoothGattCharacteristic(特征)和 BluetoothGattDescriptor(描述符)。连接到 BLE 设备始于调用 BluetoothDevice.connectGatt() — 该方法返回 BluetoothGatt,通过它执行所有后续操作。所有 GATT 回调都通过 BluetoothGattCallback 接收 — 这是一个异步接口,在创建 BluetoothGatt 的同一线程中调用。重要提示:所有 GATT 操作必须按顺序执行 — 在同一 BluetoothGatt 上同时调用多个操作会导致错误。

GATT 连接的生命周期

正确管理 GATT 连接的生命周期对于 BLE 应用程序的稳定性至关重要。调用 connectGatt() 后,应用程序等待状态为 STATE_CONNECTED 的 onConnectionStateChange() 回调。然后系统自动启动服务发现,完成后调用 onServicesDiscovered()。只有在此之后才能执行读取、写入和订阅。工作完成后,务必在 onDestroy() 或 onPause() 中调用 gatt.close() — 未关闭的 GATT 连接会消耗电池,并可能阻止在 Android 上重新连接到同一设备。

kotlin
// 通过 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) }
    }
}

标准 GATT 协议和服务

蓝牙 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 服务

如果标准服务无法满足任务需求,开发人员可以创建自己的 GATT 服务,使用 128 位 UUID(自定义 UUID)。在设计自定义服务时,需要:定义特征的逻辑组(例如,“锁管理服务”),为每个特征分配正确的属性(Read、Write、Notify),通过特征表示格式描述符(Characteristic Presentation Format)确定允许的值和测量单位。对于复杂协议,建议在服务中包含具有 Write 属性的 Command 特征和具有 Notify 属性的 Status 特征 — 这符合工业 BLE 应用程序中采用的 Command/Status 模式。

kotlin
// 创建自定义 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
}

常见问题

BLE 中的 GATT 是什么,有什么用?

GATT(通用属性协议)— 是蓝牙低功耗协议,定义了设备之间的数据交换结构。它用于标准化信息访问:所有 BLE 设备将数据组织成服务和特征,允许任何客户端读取传感器读数、控制设备和接收通知。

GATT 和 GAP 有什么区别?

GAP 负责 BLE 设备的发现和连接(扫描、广播、建立连接)。GATT — 负责连接后的数据交换(读取、写入、通知)。GAP 在连接前工作,GATT 在连接后工作。两个协议都是 BLE 必需的,但功能不同:GAP 是“网络层”,GATT 是“应用层”。

有哪些 GATT 服务和特征?

标准服务包括电池服务(Battery Service,180F,电量)、心率服务(Heart Rate,180D,脉搏)、设备信息服务(Device Information,180A,设备数据)。每个服务包含多个具有 16 位 UUID 的特征。开发人员可以使用 128 位 UUID 创建自定义服务用于特定任务 — 例如控制智能锁或传输健身追踪器数据。

GATT 在 Android 上如何工作?

在 Android 上,GATT 通过 BluetoothGatt(连接)、BluetoothGattService(服务)、BluetoothGattCharacteristic(特征)和 BluetoothGattDescriptor(描述符)实现。连接通过 connectGatt() 完成,之后事件通过 BluetoothGattCallback 回调接收:onConnectionStateChange、onServicesDiscovered、onCharacteristicChanged(用于通知)。所有 GATT 操作必须按顺序执行。

GATT 中的 UUID 是什么?

UUID(通用唯一标识符)— 16 位或 128 位的标识符,唯一确定 GATT 中的服务或特征。标准蓝牙 SIG 服务使用 16 位 UUID(例如 180D 对应心率服务)。开发人员的自定义服务使用 128 位 UUID(例如 12345678-1234-5678-1234-56789abcdef0)。UUID 使客户端能够在 GATT 服务器上找到所需的数据。

总结

  • GATT — 基于属性协议(ATT),通过服务、特征和描述符的层次结构进行结构化数据交换的 BLE 协议
  • 数据层次结构 — 服务(逻辑组)、特征(带类型的特定值)、描述符(元数据 — 单位、通知设置)
  • GATT 角色 — 服务器(提供数据,通常是 BLE 外设)和客户端(读取/写入,通常是 Android 智能手机)
  • 操作 — 读取(Read)、写入(Write)、通知(Notify,无需确认,更快)和指示(Indicate,带确认,更可靠);选择取决于场景
  • Android API — BluetoothGatt、BluetoothGattCallback、BluetoothGattService、BluetoothGattCharacteristic;通过回调进行顺序异步调用
  • 标准协议 — 电池服务、心率服务、设备信息服务 — 确保不同制造商设备之间的兼容性
  • 自定义服务 — 使用 128 位 UUID 用于独特任务;按照 Command/Status 模式设计,具有正确的属性(Read、Write、Notify、Indicate)

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读