蓝牙 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 的智能设备交互!

相关推荐
invicinble26 分钟前
认识es的多个维度
android·大数据·elasticsearch
前端切图仔00135 分钟前
Chrome 扩展程序上架指南
android·java·javascript·google
黄林晴38 分钟前
Compose Multiplatform 1.10.0 重磅发布!三大核心升级,跨平台开发效率再提升
android·android jetpack
锁我喉是吧40 分钟前
Android studio 编译faiss
android·android studio·faiss
鹏程十八少43 分钟前
3. Android 腾讯开源的 Shadow,凭什么成为插件化“终极方案”?
android·前端·面试
TheNextByte11 小时前
如何通过蓝牙将联系人从Android传输到 iPhone
android·cocoa·iphone
Wpa.wk1 小时前
性能测试-性能监控相关命令-基础篇
android·linux·运维·经验分享·测试工具·性能测试·性能监控
Kapaseker1 小时前
必须要搞懂的 View 核心问题
android·kotlin
运筹vivo@1 小时前
攻防世界: simple_php
android·php·android studio
2501_915921431 小时前
360移动端性能监控实践QDAS-APM(iOS篇)
android·macos·ios·小程序·uni-app·cocoa·iphone