蓝牙 GATT 协议及其在 Android 的实现

文章目录

  • [1. 什么是蓝牙 GATT 协议?](#1. 什么是蓝牙 GATT 协议?)
    • [1.1 GATT 的核心组件](#1.1 GATT 的核心组件)
    • [1.2 GATT 的主要操作](#1.2 GATT 的主要操作)
  • [2. GATT 协议在 Android 上的实现](#2. GATT 协议在 Android 上的实现)
    • 代码解释
      • [2.1. BluetoothGattManager 类](#2.1. BluetoothGattManager 类)
      • [2.2. 连接蓝牙设备](#2.2. 连接蓝牙设备)
      • 2.3.服务发现
      • [2.4. 读取特征](#2.4. 读取特征)
      • [2.5. 写入特征](#2.5. 写入特征)
      • [2.6. 启用通知](#2.6. 启用通知)
      • [2.7. 错误处理与资源管理](#2.7. 错误处理与资源管理)
  • [3. 常见问题与解决方案](#3. 常见问题与解决方案)
    • [3.1 连接成功,但无法读取或写入特征](#3.1 连接成功,但无法读取或写入特征)
    • [3.2 特征通知延迟或丢失](#3.2 特征通知延迟或丢失)
    • [3.3 设备无法支持高频率通知](#3.3 设备无法支持高频率通知)
  • 总结

1. 什么是蓝牙 GATT 协议?

蓝牙 GATT(Generic Attribute Profile,通用属性配置文件)是基于低功耗蓝牙(Bluetooth Low Energy, BLE)的一个重要协议,用于定义如何在设备之间传输数据。它是 BLE 的核心协议之一,广泛应用于智能手环、蓝牙传感器等 IoT 设备。

1.1 GATT 的核心组件

  • Server(服务端):提供数据的设备(如蓝牙传感器)。
  • Client(客户端):读取或写入数据的设备(如手机)。
  • Service(服务):表示某个功能,如 Heart Rate Service(心率服务),包含多个特征。
  • Characteristic(特征):特征是服务中的数据单元,包含数据和权限(如读、写、通知)。
  • Descriptor(描述符):用于描述特征的元数据,例如单位、范围等。

1.2 GATT 的主要操作

  • Discover Services:发现可用服务。
  • Read/Write Characteristic:读取或写入特征值。
  • Enable Notifications/Indications:订阅特征的通知。
  • Execute Write:执行批量写操作。

2. GATT 协议在 Android 上的实现

下面是一个详细的 GATT 协议在 Android 上的实现示例,包括相关的注释。这个实现涵盖了连接蓝牙设备、发现服务、读取和写入特征、启用通知等操作。为了确保代码清晰,我将会详细解释每一部分的作用。

kotlin 复制代码
class BluetoothGattManager(private val context: Context) {

    private var bluetoothGatt: BluetoothGatt? = null
    private var bluetoothDevice: BluetoothDevice? = null

    // GATT 回调,用于处理与蓝牙设备的交互
    private val gattCallback = object : BluetoothGattCallback() {
        
        // 连接状态发生变化时的回调
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.d("BluetoothGatt", "Connected to GATT server")
                // 连接后开始发现服务
                gatt.discoverServices()
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.d("BluetoothGatt", "Disconnected from GATT server")
                // 连接断开时关闭 GATT
                gatt.close()
            }
        }

        // 服务发现时的回调
        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d("BluetoothGatt", "Services discovered")
                // 在此可以选择读取服务或特征
                // 例如读取心率测量特征
                val heartRateService = gatt.getService(UUID.fromString(HEART_RATE_SERVICE_UUID))
                val heartRateMeasurement = heartRateService?.getCharacteristic(UUID.fromString(HEART_RATE_MEASUREMENT_UUID))
                gatt.readCharacteristic(heartRateMeasurement)
            } else {
                Log.e("BluetoothGatt", "Service discovery failed, status: $status")
            }
        }

        // 读取特征值时的回调
        override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d("BluetoothGatt", "Characteristic read successfully: ${characteristic.value}")
                // 可以处理读取的数据
            } else {
                Log.e("BluetoothGatt", "Read failed, status: $status")
            }
        }

        // 写入特征值时的回调
        override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d("BluetoothGatt", "Characteristic written successfully: ${characteristic.value}")
            } else {
                Log.e("BluetoothGatt", "Write failed, status: $status")
            }
        }

        // 特征值的通知回调
        override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
            Log.d("BluetoothGatt", "Characteristic changed: ${characteristic.value}")
            // 可以处理通知数据
        }

        // 描述符的写入回调
        override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d("BluetoothGatt", "Descriptor written successfully")
            } else {
                Log.e("BluetoothGatt", "Descriptor write failed, status: $status")
            }
        }
    }

    // 蓝牙设备连接
    fun connectToDevice(device: BluetoothDevice) {
        bluetoothDevice = device
        bluetoothGatt = device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
    }

    // 断开与设备的连接
    fun disconnect() {
        bluetoothGatt?.disconnect()
    }

    // 开始读取特征数据
    fun readHeartRateMeasurement() {
        val heartRateService = bluetoothGatt?.getService(UUID.fromString(HEART_RATE_SERVICE_UUID))
        val heartRateMeasurement = heartRateService?.getCharacteristic(UUID.fromString(HEART_RATE_MEASUREMENT_UUID))
        bluetoothGatt?.readCharacteristic(heartRateMeasurement)
    }

    // 开始写入特征数据
    fun writeHeartRateMeasurement(value: ByteArray) {
        val heartRateService = bluetoothGatt?.getService(UUID.fromString(HEART_RATE_SERVICE_UUID))
        val heartRateMeasurement = heartRateService?.getCharacteristic(UUID.fromString(HEART_RATE_MEASUREMENT_UUID))
        heartRateMeasurement?.value = value
        bluetoothGatt?.writeCharacteristic(heartRateMeasurement)
    }

    // 启用特征通知
    fun enableHeartRateNotifications() {
        val heartRateService = bluetoothGatt?.getService(UUID.fromString(HEART_RATE_SERVICE_UUID))
        val heartRateMeasurement = heartRateService?.getCharacteristic(UUID.fromString(HEART_RATE_MEASUREMENT_UUID))

        bluetoothGatt?.setCharacteristicNotification(heartRateMeasurement, true)

        // 启用通知描述符
        val descriptor = heartRateMeasurement?.getDescriptor(UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG_UUID))
        descriptor?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
        bluetoothGatt?.writeDescriptor(descriptor)
    }

    // UUID 常量定义
    companion object {
        const val HEART_RATE_SERVICE_UUID = "0000180D-0000-1000-8000-00805f9b34fb"  // 心率服务 UUID
        const val HEART_RATE_MEASUREMENT_UUID = "00002A37-0000-1000-8000-00805f9b34fb"  // 心率测量特征 UUID
        const val CLIENT_CHARACTERISTIC_CONFIG_UUID = "00002902-0000-1000-8000-00805f9b34fb"  // 通知描述符 UUID
    }
}

代码解释

2.1. BluetoothGattManager 类

该类封装了与蓝牙 GATT 设备交互的基本操作,包括连接、断开连接、读取/写入特征、启用通知等。

  • BluetoothGatt:用于与远程设备的 GATT 服务器进行交互。
  • BluetoothGattCallback:所有 GATT 操作的回调,包括连接状态、服务发现、特征操作等。

2.2. 连接蓝牙设备

通过调用 connectToDevice 方法,我们可以通过 BluetoothDevice 对象来连接到远程设备。连接过程是异步的,结果会通过 BluetoothGattCallback 回调来处理。

kotlin 复制代码
bluetoothGatt = device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
  • 参数:
    context : 当前应用的上下文。
    false : 不使用自动连接。
    gattCallback : 回调,用于处理 GATT 事件。
    TRANSPORT_LE: 指定蓝牙低功耗模式。

2.3.服务发现

连接设备后,调用 gatt.discoverServices() 发现远程设备提供的所有服务。发现完成后,onServicesDiscovered 回调会被触发。

2.4. 读取特征

通过 readCharacteristic 读取特征值。特征是通过服务来获取的,必须先通过 UUID 获取对应的服务,再获取特征。

kotlin 复制代码
val heartRateMeasurement = heartRateService?.getCharacteristic(UUID.fromString(HEART_RATE_MEASUREMENT_UUID))
bluetoothGatt?.readCharacteristic(heartRateMeasurement)

2.5. 写入特征

通过 writeCharacteristic 向设备写入数据。例如,将某个测量值写入心率测量特征。

kotlin 复制代码
bluetoothGatt?.writeCharacteristic(heartRateMeasurement)

2.6. 启用通知

通过 setCharacteristicNotification 启用特征通知,并通过描述符使能通知。
这意味客户端(如 Android 设备)希望接收该特征的通知

kotlin 复制代码
bluetoothGatt?.setCharacteristicNotification(heartRateMeasurement, true)
val descriptor = heartRateMeasurement?.getDescriptor(UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG_UUID))
descriptor?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
bluetoothGatt?.writeDescriptor(descriptor)

2.7. 错误处理与资源管理

  • 使用 onConnectionStateChange 处理连接和断开的状态。
  • 使用 onCharacteristicRead, onCharacteristicWrite, onCharacteristicChanged 处理特征的读写和通知。
  • 在连接断开时,调用 gatt.close() 来释放 GATT 连接资源。

3. 常见问题与解决方案

3.1 连接成功,但无法读取或写入特征

问题描述:

连接到设备后,客户端可以成功连接,但无法读取或写入特征。

解决方案:
1、检查 GATT 特征是否正确支持读取/写入 :确保在 GATT 服务的特征中定义了适当的权限(例如 READ 或 WRITE)。
2、检查特征的通知和描述符:如果启用了通知,需要确保已设置正确的描述符来启用通知(例如 Client Characteristic Configuration Descriptor)。

kotlin 复制代码
val descriptor = characteristic?.getDescriptor(UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG_UUID))
descriptor?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
bluetoothGatt?.writeDescriptor(descriptor)

3、延迟操作:读取和写入操作可能会被异步处理,确保在操作完成后再进行后续操作。使用 BluetoothGattCallback 来监控操作的结果。

3.2 特征通知延迟或丢失

问题描述:

设备启用了特征通知,但客户端应用接收到的数据有延迟或丢失。
解决方案:
1、检查蓝牙连接稳定性 :不稳定的蓝牙连接可能导致通知丢失或延迟。保持设备与客户端之间的距离适当,并确保没有干扰源。
2、优化 GATT 特征的通知频率 :有些设备在频繁发送通知时会出现延迟或丢失,尝试减少通知频率或增加缓冲时间。
3、使用可靠的通知机制:在某些情况下,可以通过增加 writeCharacteristic 的可靠性(例如启用重试机制)来减少数据丢失。

3.3 设备无法支持高频率通知

问题描述:

某些低功耗蓝牙设备(如传感器)可能无法支持非常频繁的通知,导致连接质量差或丢失。

解决方案:
1、降低通知频率 :对于不支持高频通知的设备,尝试降低通知频率,避免过高的请求频率对设备性能的影响。
2、优化通信协议:使用适当的通信协议来缓解高频率通知带来的负担,如分批发送数据。

总结

蓝牙 GATT 协议在低功耗蓝牙通信 中至关重要。

Android BLE 实现提供了丰富的接口,可以灵活实现扫描、连接、数据交互等功能。

开发过程中需重点关注设备兼容性权限通信稳定性

通过掌握以上内容,你可以轻松实现基于 BLE 的智能设备交互!

相关推荐
戏谑32 分钟前
Android 常用布局
android·view
拭心12 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王14 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡15 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道15 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库16 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道16 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe17 小时前
Android Hook - 动态加载so库
android
居居飒17 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He20 小时前
桌面列表小部件不能点击的问题分析
android