BluetoothGatt — Android 类,提供通过 BLE 连接运行 GATT 客户端(通用属性协议)的 API。BluetoothGatt 封装了与远程 GATT 服务器(外围 BLE 设备)的连接,并管理所有配置文件操作:发现服务、读写特征、订阅通知和指示。BluetoothGatt 实例通过 BluetoothDevice.connectGatt() 及 BluetoothGattCallback 回调获得。根据 Android Developers, 2026,BluetoothGatt 是双向 BLE 通信的核心类,支持从 BLE 4.0 到 BLE 5.4 的 GATT 操作。
要点
BluetoothGatt — 是一个代理对象,表示 Android 设备(中央)与 BLE 外设(服务器)之间的 GATT 连接。每个 BluetoothGatt 实例对应一个活动的 BLE 连接。通过它执行所有 GATT 配置文件操作:发现、读取、写入、通知。BluetoothGatt 不直接创建——它由 BluetoothDevice.connectGatt() 方法返回。
创建 BluetoothGatt 需要四个参数。Context — 应用程序上下文(Activity 或 Application)。autoConnect — 如果为 false,Android 立即发起直接连接;如果为 true,Android 在检测到设备时自动连接(适用于后台连接)。BluetoothGattCallback — 所有 GATT 事件的必需回调。transport — BluetoothDevice.TRANSPORT_LE(BLE)或 TRANSPORT_BREDR(Classic)。在 BLE 设备上始终使用 TRANSPORT_LE。
BluetoothGatt 生命周期 由五个状态组成。DISCONNECTED — 初始状态。CONNECTING — 调用 connectGatt 后,在确认之前。CONNECTED — 在 onConnectionStateChange 返回 STATE_CONNECTED 之后。连接后调用 discoverServices() 获取 GATT 层次结构。工作完成后调用 disconnect() 和 close() 以释放系统资源。如果不调用 close(),应用程序可能会耗尽 Android BLE 连接限制(通常为 4–8 个)。
// 创建 BluetoothGatt 连接
import android.bluetooth.*
class GattConnector(private val context: Context) {
private var bluetoothGatt: BluetoothGatt? = null
fun connect(device: BluetoothDevice): BluetoothGatt? {
// 关闭已有连接(如果存在)
close()
bluetoothGatt = device.connectGatt(
context,
false, // autoConnect = false ( )
object : BluetoothGattCallback() {
override fun onConnectionStateChange(
gatt: BluetoothGatt, status: Int, newState: Int
) {
when (newState) {
BluetoothProfile.STATE_CONNECTED -> {
// 2. 连接已建立 → 发现
gatt.discoverServices()
}
BluetoothProfile.STATE_DISCONNECTED -> {
// 3. 连接丢失
close()
}
}
}
override fun onServicesDiscovered(
gatt: BluetoothGatt, status: Int
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 4. 已接收 GATT 层次结构
onGattReady(gatt)
}
}
},
BluetoothDevice.TRANSPORT_LE
)
return bluetoothGatt
}
private fun onGattReady(gatt: BluetoothGatt) {
// GATT 准备好进行读写操作
}
fun close() {
bluetoothGatt?.disconnect()
bluetoothGatt?.close()
bluetoothGatt = null
}
}
GattConnector 类演示了正确的 BluetoothGatt 创建。参数 autoConnect=false — 直接连接(用于已扫描的设备)。在 onConnectionStateChange 返回 STATE_CONNECTED 时立即调用 discoverServices()。onServicesDiscovered 表示 GATT 已就绪。close() 依次调用 disconnect() 和 close()——若不调用 close(),系统资源无法释放,导致 BLE 连接泄漏。
discoverServices() — 连接后调用的第一个 GATT 方法。在 BLE 外设上启动所有服务的异步搜索。结果通过 onServicesDiscovered() 返回状态码:GATT_SUCCESS (0) — 成功,133 — GATT_ERROR,8 — GATT_CONNECTION_TIMEOUT。成功发现后,BluetoothGatt 填充通过 getServices() 可用的服务列表。
每个 BluetoothGattService 包含一个 BluetoothGattCharacteristic 列表。特征具有 UUID、属性(PROPERTY_READ、PROPERTY_WRITE、PROPERTY_NOTIFY)和可选的描述符。属性决定允许哪些操作:如果特征没有 PROPERTY_READ,调用 readCharacteristic 将返回错误。使用 getDescriptors() 获取特征的描述符。
// 服务发现和特征搜索
class GattServiceExplorer {
// 按 UUID 查找服务
fun findService(gatt: BluetoothGatt, uuid: UUID): BluetoothGattService? {
return gatt.services?.firstOrNull { it.uuid == uuid }
}
// 在服务中查找特征
fun findCharacteristic(
service: BluetoothGattService,
uuid: UUID
): BluetoothGattCharacteristic? {
return service.characteristics?.firstOrNull { it.uuid == uuid }
}
// 获取所有支持的特征操作
fun getCharacteristicProperties(chars: BluetoothGattCharacteristic): List<String> {
val props = mutableListOf<String>()
with(chars.properties) {
if (and(BluetoothGattCharacteristic.PROPERTY_READ) != 0) props.add("READ")
if (and(BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) props.add("WRITE")
if (and(BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0) props.add("WRITE_NO_RESP")
if (and(BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) props.add("NOTIFY")
if (and(BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) props.add("INDICATE")
}
return props
}
// 记录完整 GATT 层次结构
fun dumpGattTree(gatt: BluetoothGatt) {
gatt.services?.forEach { service ->
print("Service: ${service.uuid}")
service.characteristics?.forEach { char ->
print(" Characteristic: ${char.uuid}, properties: ${char.properties}")
char.descriptors?.forEach { desc ->
print(" Descriptor: ${desc.uuid}")
}
}
}
}
}
GattServiceExplorer 类提供在 GATT 层次结构中导航的工具。findService 和 findCharacteristic 按 UUID 搜索服务和特征。getCharacteristicProperties 通过 and() 检查属性的位掩码。dumpGattTree 将完整层次结构输出到日志中——有助于调试 BLE 设备。所有对 BluetoothGatt 的操作必须在成功的 onServicesDiscovered 之后执行,否则 getServices() 将返回空列表。
readCharacteristic() — BluetoothGatt 的异步方法,用于从远程 BLE 设备读取特征值。结果在 BluetoothGattCallback 的 onCharacteristicRead() 中返回。如果设备上有 2+ 个匹配的特征(可能性很小但存在),readCharacteristic() 可能读取到非目标特征——更安全的方式是在 BluetoothGattCharacteristic 实例上调用 readCharacteristic(),而不是按 UUID 调用。
readDescriptor() — 读取特征描述符值的方法。典型的描述符是 CCCD(客户端特征配置描述符,UUID 0x2902),它确定通知是否已启用。结果在 onDescriptorRead() 中返回。在实践中很少需要读取描述符——CCCD 由 setCharacteristicNotification() 管理,但对于自定义描述符(User Description 0x2901、Presentation Format 0x2904),readDescriptor() 是获取元数据的唯一方式。
MTU 与大数据读取 — 如果特征值超过 MTU(BLE 4.0 为 23 字节),Android 会自动通过 BLE 堆栈的一系列读取请求对数据进行分片和重组。对于 BLE 5.0+ 扩展 MTU(最多 251 字节),不需要分片——一次读取即可返回完整数据。在读取前可以调用 requestMtu() 协商最大 MTU。
// 通过 BluetoothGatt 读取特征和描述符
class GattReader {
fun readHeartRate(gatt: BluetoothGatt) {
// 心率服务 UUID = 0x180D
val service = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
?: return
// 心率测量 UUID = 0x2A37
val characteristic = service.getCharacteristic(
UUID.fromString("00002A37-0000-1000-8000-00805F9B34FB")
) ?: return
if (hasProperty(characteristic, BluetoothGattCharacteristic.PROPERTY_READ)) {
gatt.readCharacteristic(characteristic)
}
}
fun readDescriptor(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
// CCCD(0x2902)
val cccd = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
)
cccd?.let { gatt.readDescriptor(it) }
}
// 在 onCharacteristicRead 回调中处理数据:
fun parseHeartRate(value: ByteArray): Int {
// BLE 心率:字节 = 标志位,第二 = bpm
return if (value.isNotEmpty()) value[1].toInt() and 0xFF else 0
}
private fun hasProperty(char: BluetoothGattCharacteristic, prop: Int): Boolean {
return char.properties and prop != 0
}
}
GattReader 类演示了心率特征的读取。服务 0x180D 包含特征 0x2A37(心率测量)——Bluetooth SIG 的标准 BLE 配置文件。读取前通过 hasProperty 检查 PROPERTY_READ 属性。parseHeartRate 解析 BLE 心率格式:第一个字节是 flags(数据格式),第二个是 bpm 值。读取 CCCD 描述符(0x2902)以检查通知状态。
writeCharacteristic() — BluetoothGatt 向 BLE 外设写入数据的方法。在 Android API 33+ 上,writeCharacteristic() 已被 writeCharacteristic(request) 取代,其中 BluetoothGattCharacteristicWriteRequest 是一个请求对象,包含特征、字节数组和 WriteType。旧的带 setValue() 的 writeCharacteristic(characteristic) 方法已弃用。WriteType 确定请求的行为:WRITE_TYPE_DEFAULT(取决于特征属性)、WRITE_TYPE_NO_RESPONSE(无响应)和 WRITE_TYPE_SIGNED(授权)。
WriteType 的选择影响速度和可靠性。WRITE_TYPE_DEFAULT 通常对应 withResponse(如果特征具有 PROPERTY_WRITE)或 withoutResponse(如果具有 PROPERTY_WRITE_NO_RESPONSE)。对于流式数据(OTA 更新、日志),使用 WRITE_TYPE_NO_RESPONSE——最大带宽。对于需要保证交付的命令(启用、配置)——使用带 onCharacteristicWrite 确认的 WRITE_TYPE_DEFAULT。
// 在 Android API 33+ 上写入 BLE 特征
class GattWriter {
// 带响应的写入(withResponse)
fun writeWithResponse(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, data: ByteArray) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// API 33+:writeCharacteristic
val request = BluetoothGattCharacteristicWriteRequest(
characteristic,
data,
BluetoothGattCharacteristicWriteRequest.WRITE_TYPE_DEFAULT,
@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
)
gatt.writeCharacteristic(request)
} else {
// API < 33: (deprecated)
characteristic.setValue(data)
gatt.writeCharacteristic(characteristic)
}
}
// 无响应写入(最大速度)
fun writeWithoutResponse(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, data: ByteArray) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val request = BluetoothGattCharacteristicWriteRequest(
characteristic,
data,
BluetoothGattCharacteristicWriteRequest.WRITE_TYPE_NO_RESPONSE,
@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
)
gatt.writeCharacteristic(request)
} else {
characteristic.setValue(data)
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
gatt.writeCharacteristic(characteristic)
}
}
// onCharacteristicWrite 回调(API 33+)
private val writeCallback = object : BluetoothGattCallback() {
override fun onCharacteristicWrite(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
status: Int,
callbackType: Int
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
print("Write success: ${value.size} bytes")
}
}
}
}
GattWriter 类支持不同 API 级别的两种 WriteType。writeWithResponse 使用 WRITE_TYPE_DEFAULT——BLE 设备通过 onCharacteristicWrite 确认写入。writeWithoutResponse 使用 WRITE_TYPE_NO_RESPONSE——数据无需确认即可发送,最大带宽。在 API 33+ 上使用带 BluetoothGattCharacteristicWriteRequest 的新 writeCharacteristic(request)。在 API < 33 上使用旧的 setValue() + writeCharacteristic()。
setCharacteristicNotification() — BluetoothGatt 订阅外设特征变化通知的方法。订阅激活后,BLE 设备通过 onCharacteristicChanged() 发送新值。然而,setCharacteristicNotification() 仅激活 Android 本地通知——要在 BLE 设备本身上启用通知,还需要将值 0x0100 写入 CCCD 描述符(0x2902)。
CCCD(客户端特征配置描述符) — 管理从 BLE 外设发送通知的描述符。值 0x0000 — 通知关闭,0x0100 — 通知开启(notifications),0x0200 — 指示开启(indications)。写入 CCCD 在调用 setCharacteristicNotification() 后通过 BluetoothGatt 上的 writeDescriptor() 完成。Android 不会自动写入 CCCD——这一责任在于开发者。
// 正确的 BLE 通知订阅
class GattNotificationManager {
// 1. 启用通知
fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
// 步骤 1:Android 本地订阅
val success = gatt.setCharacteristicNotification(characteristic, true)
if (!success) {
print("订阅失败")
return
}
// 步骤 2:在 BLE 设备上写入 CCCD(0x2902)
val cccdDescriptor = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
) ?: return
// 0x0100 = 通知,0x0200 = 指示
val cccdValue = if (characteristic.properties
and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) {
BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE // [0x01, 0x00]
} else {
BluetoothGattDescriptor.ENABLE_INDICATION_VALUE // [0x02, 0x00]
}
cccdDescriptor.setValue(cccdValue)
gatt.writeDescriptor(cccdDescriptor)
}
// 2. 特征发现
fun disableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
gatt.setCharacteristicNotification(characteristic, false)
val cccdDescriptor = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
) ?: return
cccdDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)
gatt.writeDescriptor(cccdDescriptor)
}
// 3. 通知回调
private val notificationCallback = object : BluetoothGattCallback() {
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
callbackType: Int
) {
// 来自 BLE 外设的新值
print("Notification: ${value.size} bytes")
}
}
}
GattNotificationManager 类实现了正确的两步 BLE 通知订阅协议。enableNotification 首先在 Android 上调用 setCharacteristicNotification(true),然后通过 writeDescriptor 将 0x0100 写入 CCCD 描述符。disableNotification 执行相反的操作。如果不写入 CCCD,BLE 设备不会发送通知——这是 Android BLE 开发人员最常见的错误。
完整示例 是 Kotlin 中的 GATT 客户端,它将创建 BluetoothGatt、发现、读取和订阅通知结合在一个管理器中使用协程进行异步处理。
// 在 Kotlin 中使用协程的完整 GATT 客户端
class GattClient(context: Context) {
private val context = context.applicationContext
private var gatt: BluetoothGatt? = null
// 使用协程连接
suspend fun connect(device: BluetoothDevice): Boolean =
suspendCoroutine { continuation ->
gatt = device.connectGatt(
context, false,
object : BluetoothGattCallback() {
override fun onConnectionStateChange(
gatt: BluetoothGatt, status: Int, newState: Int
) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices()
} else {
continuation.resume(false)
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
continuation.resume(status == BluetoothGatt.GATT_SUCCESS)
}
},
BluetoothDevice.TRANSPORT_LE
)
}
// 通过协程读取特征
suspend fun readCharacteristicValue(char: BluetoothGattCharacteristic): ByteArray? =
suspendCoroutine { continuation ->
gatt?.let { gatt ->
// 保存特征标签用于回调识别
gatt.setCharacteristic(char, null) // 适用于 API < 33
gatt.readCharacteristic(char)
}
}
// 关闭连接
fun release() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
GattClient GATT 客户端使用协程(suspendCoroutine)将基于回调的 BluetoothGatt API 转换为顺序调用。connect() 等待 onServicesDiscovered,之后 GATT 层次结构可用。readCharacteristicValue() 等待 onCharacteristicRead。这种方法消除了嵌套回调并使 BLE 代码线性化。release() 确保资源释放——在 Activity 的 onDestroy 或 ViewModel.onCleared 中必须调用。
常见问题
BluetoothGatt — Android GATT 客户端的类,管理与外围设备的 BLE 连接。通过 BluetoothDevice.connectGatt() 创建,提供 discoverServices()、readCharacteristic()、writeCharacteristic()、setCharacteristicNotification() 方法。所有操作的结果通过 BluetoothGattCallback 异步返回。没有 BluetoothGatt,Android 上的双向 BLE 通信是不可能的。
状态 133(GATT_ERROR)表示 Android BLE 堆栈的内部错误。原因:设备在发现过程中断开、MTU 小于最小值(23 字节)或 BLE 堆栈过载。解决方案:延迟 500 毫秒后重试 discoverServices(),检查设备的 RSSI,并确保外设在当前状态下支持 GATT 发现。
要写入带确认的特征,请调用 writeCharacteristic() 并指定 WRITE_TYPE_DEFAULT(API 33+:BluetoothGattCharacteristicWriteRequest)。成功后,BLE 设备发送确认,Android 调用 onCharacteristicWrite 并返回 GATT_SUCCESS。如果设备在 30 秒内未响应(堆栈超时),回调返回错误状态。对于 watchdog,使用带 postDelayed 的 Handler。
在 Android 13+(API 33)上,BluetoothGatt 的方法已更改:writeCharacteristic() 现在接受 BluetoothGattCharacteristicWriteRequest,readCharacteristic() 接受 BluetoothGattCharacteristicReadRequest。旧的 setValue()/writeCharacteristic() 已弃用。BluetoothGattCallback 也已更改:onCharacteristicRead()、onCharacteristicWrite()、onCharacteristicChanged() 接收 ByteArray 值和 callbackType。使用 Build.VERSION.SDK_INT 进行分支处理。
Android 支持 4–8 个同时 BLE-GATT 连接(取决于制造商和 Android 版本)。Pixel/Google:最多 7 个,Samsung:最多 5 个,Xiaomi:最多 4 个。超过限制时,connectGatt 返回 null 或 onConnectionStateChange 返回错误。如需与大量设备配合使用,请使用循环连接或 Bluetooth Mesh。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。