android ble 的各种坑

这段时间在做低功耗蓝牙 (BLE) 应用的开发(并不涉及蓝牙协议栈)。总体感觉 Android BLE 还是不太稳定,开发起来也是各种痛苦。这里记录一些杂项和开发中遇到的问题及其解决方法,避免大家踩坑。本文说的问题有些没有得到官方文档的验证,不过也有一些论坛帖子的支持,也可以算是有一定根据。

  1. Android 从 4.3(API Level 18) 开始支持低功耗蓝牙,但是只支持作为中心设备 (Central) 模式,这就意味着 Android 设备只能主动扫描和链接其他外围设备 (Peripheral)。从Android 5.0(API Level 21)开始两种模式都支持。BLE 官方文档在  这里 。 

  2. 在  BluetoothAdapter.startLeScan() 的时候,在 BluetoothAdapter.LeScanCallback.onLeScan() 中不能做太多事情,特别是周围的BLE设备多的时候,非常容易导致出现如下错误: 

    E/GKI  LINUX(17741): ##### ERROR : GKI exception: GKI  exception(): Task State Table E/GKI LINUX(17741): ##### 

    E/GKI  LINUX(17741): ##### ERROR : GKI exception: TASK ID [0] task name [BTU] state [1] 

    E/GKI 

    LINUX(17741): #####

    LINUX(17741): ##### ERROR : GKI 

    exception: TASK ID [1] task name [BTIF] state [1]

    LINUX(17741): ##### 

    E/GKI  LINUX(17741): ##### ERROR : GKI exception: TASK ID [2] task name [A2DP-MEDIA] state [1] 

    E/GKI 

    LINUX(17741): #####

    LINUX(17741): ##### ERROR : GKI   exception: GKI exception 65524 getbuf: out of buffers##### 

    E/GKI  LINUX(17741): ##### ERROR : GKI exception: 

    E/GKI_LINUX(17741):  * * * * * * * * * * * * * * * * * * * * * * 

    开发建议:在  onLeScan() 回调中只做尽量少的工作,可以把扫描到的设备,扔到另外一个线程中去处理,让  onLeScan() 尽快返回。  [  参考帖子 ] 

  3. 在使用  BluetoothDevice.connectGatt() 或者 BluetoothGatt.connect() 等建立  BluetoothGatt 连接的时候,在任何时刻都只能最多一个设备在尝试建立连接。如果同时对多个蓝牙设备发起建立 Gatt 连接请求。如果前面的设备连接失败了,后面的设备请求会被永远阻塞住,不会有任何连接回调。 

    开发建议:如果要对多个设备发起连接请求,最好是有一个同一个的设备连接管理,把发起连接请求序列化起来。前一个设备请求建立连接,后面请求在队列中等待。如果连接成功了,就处理下一个连接请求。如果连接失败了(例如出错,或者连接超时失败),就马上调用  BluetoothGatt.disconnect() 来释放建立连接请求,然后处理下一个设备连接请求。  [  参考帖子 ] 

  4. 对 BluetoothGatt 操作  (read/write)Characteristic() , (read/write)Descriptor() 和  readRemoteRssi() 都是异步操作。需要特别注意的是,同时只能有一个操作(有些贴这说只能同时有一个 writeCharacteristic() ,这个我并没有严格验证),也就是等上一个操作回调(例如  onCharacteristicWrite() )以后,再进行下一个操作。 

    开发建议:把这写操作都封装成同步操作,一个操作回调之前,阻塞主其他调用。  [  参考帖子 ] 

  5. BLE 设备的建立和断开连接的操作,例如 BluetoothDevice.connectGatt() ,  BluetoothGatt.connect() , BluetoothGatt.disconnect() 等操作最好都放在主线程中,否则你会遇到很多意想不到的麻烦。 

    开发建议:对  BluetoothGatt 的连接和断开请求,都通过发送消息到 Android 的主线程中,让主线程来执行具体的操作。例如创建一个  new Handler(context.getMainLooper()); ,把消息发送到这个  Handler中。  [  参考帖子 ] 

  6. 如果你在开发 BLE 应用的时候,有时候会发现系统的功耗明显增加了,查看电量使用情况,蓝牙功耗占比非常高,好像低功耗是徒有虚名。使用  adb bugreport 获取的了系统信息,分析发现一个名叫 BluetoothRemoteDevices 的  WakeLock 锁持有时间非常长,导致系统进入不了休眠。分析源代码发现,在连接 BLE 设备的过程中,系统会持有 (Aquire) 这个  WakeLock ,直到连接上或者主动断开连接(调用  disconnect() )才会释放。如果BLE设备不在范围内,这个超时时间大约为30s,而这时你可能又要尝试重新连接,这个  WakeLock 有被重新持有,这样系统就永远不能休眠了。 

    开发建议:对BLE设备连接,连接过程要尽量短,如果连接不上,不要盲目进行重连,否这你的电池会很快被消耗掉。这个情况,实际上对传统蓝牙设备连接也是一样。  [  参考帖子 ] 

  7. Android 作为中心设备,最多只能同时连接 6 个 BLE 外围设备(可能不同的设备这个数字不一样),超过 6 个,就会连接不上了。现在 BLE 设备越来越多,其实并不够用,所以在开发的过程中,需要特别的谨慎使用。

    开发建议:按照需要连接设备,如果设备使用完了,应该马上释放连接(调用 BluetoothGatt.close() ),腾出系统资源给其他可能的设备连接。  [  参考帖子 ] 

本文只是一些经验之谈,观点也比较琐碎。这里很多问题都看起来是蓝牙协议栈不完善导致的,或许在后面 Android 升级中会修复这些问题,我这里说的可能不适用了。

<think>嗯,用户想了解Android BLE开发的详细流程步骤,而且要求最新的。首先,我需要回忆一下Android BLE的基本知识,然后看看最近有没有什么更新。记得Android从不同版本开始对BLE的支持有变化,特别是Android 12和13可能有一些权限方面的调整。 首先,应该从权限开始讲起。Android BLE需要蓝牙相关的权限,比如BLUETOOTH和BLUETOOTH_ADMIN,还有位置权限,因为扫描BLE设备需要ACCESS_FINE_LOCATION或者ACCESS_COARSE_LOCATION,不过可能从Android 12开始,位置权限的要求有变化,或者可能需要动态申请。这一点需要确认,避免过时的信息。 然后,检查蓝牙是否支持,是否开启。这部分流程应该没有太大变化,还是通过BluetoothManager获取BluetoothAdapter,然后检查是否可用,如果没开的话提示用户打开。 接下来是设备扫描,这里可能需要使用BluetoothLeScanner,并且要注意Android 5.0以上的扫描API,还有Android 6.0以上的动态权限申请。扫描时可能需要使用ScanCallback,设置扫描过滤条件和扫描设置,比如低延迟模式或者低功耗模式。 连接设备的部分,需要建立Gatt连接,使用BluetoothDevice的connectGatt方法,传入Context、autoConnect参数和BluetoothGattCallback。这里要注意不同的Android版本可能对自动连接的处理方式不同,以及可能需要在主线程执行。 连接成功后,发现服务,也就是discoverServices(),然后在回调中处理服务发现完成的情况,获取BluetoothGattService,再找到对应的特征值Characteristic。这里可能需要处理不同的服务和特征UUID,用户可能需要自己根据设备的文档来替换。 读写操作需要确保特征的可写性,比如是否有WRITE属性,或者是否需要启用通知。对于通知,需要设置特征的通知,写入Descriptor,然后监听onCharacteristicChanged回调。这部分要注意线程安全,可能需要用Handler来处理回调到主线程。 错误处理也很重要,比如连接失败、服务发现失败、读写超时等,需要适当的重试机制或者用户提示。此外,Android BLE有连接数量限制,超过的话可能会出现问题,需要提醒用户。 资源释放方面,记得在不需要的时候关闭Gatt连接,释放相关资源,避免内存泄漏。还有,Android版本差异,比如Android 7.0限制了扫描次数,或者后台扫描的限制,这些可能需要特别处理。 最新的变化可能包括Android 12(API 31)引入的BLUETOOTH_SCAN、BLUETOOTH_CONNECT和BLUETOOTH_ADVERTISE权限,这些在targetSdkVersion 31及以上时需要处理,可能需要检查是否需要NEARBY_DEVICES权限,以及是否需要声明neverForLocation属性,如果应用不需要位置信息的话。这样可以避免必须请求位置权限。 可能用户在实际开发中遇到的问题,比如扫描不到设备,连接不稳定,读写失败等,应该在步骤中提到注意事项,比如确保设备在范围内,权限正确,还有正确的UUID配置。此外,Android Studio的BLE调试工具和日志的重要性也需要提及,帮助用户调试。 总结一下,需要分步骤讲解权限、初始化、扫描、连接、服务发现、数据读写、通知设置、错误处理和资源管理,同时涵盖最新的权限变化和最佳实践。确保用户按照步骤操作能够顺利进行BLE开发,避免常见的。</think>以下为Android BLE开发的最新流程(基于Android 13及最新API): --- ### 一、环境准备 1. **硬件要求** - 手机支持BLE 4.0+(Android 4.3+) - 目标BLE设备处于广播状态 2. **Gradle配置** ```groovy android { compileSdk 34 buildFeatures { dataBinding true } } dependencies { implementation 'androidx.core:core-ktx:1.12.0' } ``` --- ### 二、权限声明 ```xml <!-- Android 12+ 需要新权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <!-- 若不需定位 --> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- Android 11及以下 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> ``` --- ### 三、核心开发流程 #### 1. 检查BLE支持 ```kotlin val bluetoothManager = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager if (!packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { showToast("设备不支持BLE") } val bluetoothAdapter = bluetoothManager.adapter ``` #### 2. 动态权限申请(Android 12+) ```kotlin val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { arrayOf( Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT ) } else { arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN ) } ActivityResultContracts.RequestMultiplePermissions().launch(permissions) ``` #### 3. 扫描BLE设备 ```kotlin val scanner = bluetoothAdapter.bluetoothLeScanner val settings = ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .build() val filter = ScanFilter.Builder() .setServiceUuid(ParcelUuid(SERVICE_UUID)) .build() scanner.startScan(listOf(filter), settings, scanCallback) private val scanCallback = object : ScanCallback() { override fun onScanResult(callbackType: Int, result: ScanResult) { // 处理扫描结果 } } ``` #### 4. 连接设备 ```kotlin val device = bluetoothAdapter.getRemoteDevice(deviceAddress) val gatt = device.connectGatt(context, false, gattCallback) private val gattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { gatt.discoverServices() // 关键步骤! } } } ``` #### 5. 发现服务与特征值 ```kotlin override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { val service = gatt.getService(SERVICE_UUID) val characteristic = service.getCharacteristic(CHARACTERISTIC_UUID) // 启用通知 gatt.setCharacteristicNotification(characteristic, true) val descriptor = characteristic.getDescriptor(CCC_DESCRIPTOR_UUID) descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE gatt.writeDescriptor(descriptor) } ``` #### 6. 数据读写 ```kotlin // 写入数据 characteristic.value = byteArrayOf(0x01, 0x02) gatt.writeCharacteristic(characteristic) // 读取数据 gatt.readCharacteristic(characteristic) // 接收数据(在回调中) override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { val data = characteristic.value } ``` --- ### 四、注意事项 1. **线程安全**:所有BLE操作应在主线程执行 2. **连接限制**:Android设备最多同时连接7个BLE设备 3. **超时处理**:建议添加5-10秒的连接超时机制 4. **资源释放**: ```kotlin fun disconnect() { gatt?.disconnect() gatt?.close() } ``` 5. **后台限制**:Android 10+限制后台扫描频率,需使用前台服务 --- ### 五、最新特性支持 1. **Phy Update**(Android 8.0+) ```kotlin gatt.readPhy() gatt.setPreferredPhy(BluetoothDevice.PHY_LE_2M, ...) ``` 2. **Periodic Advertising**(Android 10+) 3. **Direction Finding**(Android 12+需硬件支持) 建议使用最新版Android BLE库:`androidx.bluetooth:bluetooth:1.0.1` 以获得更好的API支持。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值