前言
入职不久,也是刚刚接触安卓开发。公司主要业务是嵌入式设备以及可穿戴设备。因此新人主要任务就是学习安卓蓝牙开发。
由于没有任何经验因此在开发过程中踩到了许多坑,今天将这些填坑过程分享出来,希望以后开发中避免这些问题。
(一)传递对象而不传递地址
开发之初实现主界面搜索设备,点击目标设备进入连接、通信界面的功能,因此需要传递目标设备BluetoothDevice的相关信息。起初采用Intent传递Bluetooth.getAddress()方式传递蓝牙的Mac地址,在ManagerActivity调用BluetoothDevice device = mBluetoothAdapter.getRemoteDevice()获取设备。实际使用中发现此函数比较耗时,影响效率。
解决办法:使用Intent传递BluetoothDevice对象。BluetoothDevice实现了Parcelable接口,可以使用Intent直接传递。
(二)加上延时,保证执行完毕
手机端打开蓝牙、关闭蓝牙、开启搜索和停止搜索都需要一定的时间来完成,因此在执行以上操作后最好加上一段延时,等待对应操作完成后再去执行下一步。
(三) BLE蓝牙连接参数
经典蓝牙建立连接后类似于Socket通信,出现问题的情况不多。遇到问题的情况大多数出现在BLE蓝牙中。
device.connectGatt(context, autoConnect, callback);第二个参数最好设置为false,实际测试发现设置为false连接速度更快。
(四)找准UUID,成功开启监听
BLE蓝牙的服务和特征都是通过UUID来查找的,开发时一定要区分好哪个BluetoothGattCharacteristic是用于setCharacteristicNotification的,哪个BluetoothGattCharacteristic是用于writeCharacteristic的。
setCharacteristicNotification函数用于开启监听,执行成功后相当于与设备建立通信通道,之后可以通过写入命令指令来获取设备中的数据。
setCharacteristicNotification的返回值基本上一直都是true,但是不代表开启监听成功。通过查看谷歌官方BLEdemo发现安卓系统下还需要设置用于监听下BluetoothGattCharacteristic的UUID为”00002902-0000-1000-8000-00805f9b34fb”的BluetoothGattDescriptor的Value为BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE。
当设置成功后回调onDescriptorWrite,当status == BluetoothGatt.GATT_SUCCESS基本上才能实现开启监听。而且开启监听以后最好还是要等待200ms左右再向设备发命令,等待设备和手机建立稳定通信通道。
(五)最多20字节
成功开启监听后,终于可以与设备通信了。我使用的设备是一个便携式心电计。上位机发送命令指令,下位机回复的数据格式均为64字节,然后将64字节解析出心率和心电图数据。理想很丰满,然而实际使用中才发现每次回调onCharacteristicChanged方法获取的数据最多20字节,64字节的数据分成了4包发送上来。
解决办法:只能用缓冲区接收了,判断缓冲区大小、判断缓冲区第一个字节,根据数据头读取缓冲区,并进一步解析数据。
不过值得庆幸的是上位机命令指令均小于20字节,起码写入数据不需要分包发送,如果真有遇到这种特殊情况的请看BLE分包发送。
既然最多20字节一包数据,大数据通信就不要考虑BLE了。
(六)一定要关闭连接
我就有一段程序在通信完毕后忘记了断开连接,发现之后再次连接设备以后每写入一次命令onCharacteristicChanged回调多次相同数据,使得缓冲区处理出现问题。感觉就是建立了多个连接通道。在通信出错或者完成同步之后一定要调用mBluetoothGatt.disconnect();
mBluetoothGatt.close();
断开当前连接。
(七)加密BLE设备
大多数BLE设备是非加密的,也就是不需要与手机配对即可通信。大部分软件都是同步BLE设备的中数据到手机端,整个流程也是自动执行的。大体流程就是:扫描目标设备、开启连接、连接成功扫描服务,扫到服务开启监听、收发数据、断开连接。
BLE设备加密后在连接设备时会弹出系统蓝牙配对对话框,等待用户配对。按常理说此时设备是没有建立连接的,因为还没有配对成功,但是安卓手机端不仅可以完成建立连接,而且还能扫描到服务。如果按照自动流程就去开启监听是无法成功的,因为设备还没有配对成功。
解决办法:监听系统关于蓝牙配对的广播,在完成配对之后在去开启监听。如果是已经配对的设备不会由蓝牙配对广播,只需按照不加密设备流程执行即可。
(八)6.0以上权限问题
最初测试用的设备比较老旧,有天用新手机测试发现根本不能扫描到蓝牙设备,查询之后发现是权限问题。安卓6.0以后扫描蓝牙需要模糊定位权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
安卓随着版本提升对于权限限制更加严格,还是学着用动态申请权限吧。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
(九)碎片化带来各种问题
安卓系统碎片化导致遇到了各种奇葩问题:
华为mate8 mate9 荣耀6a无法主动断开与加密BLE设备连接,除非取消配对或者关闭某一方的蓝牙。
魅族Flyme系统第一次配对成功后不能断开连接,以后使用可以正常断开。
部分手机在BLE设备连接上之后会主动断开一次连接,然后马上又重新连接上设备。
结语
以上就是本人在安卓蓝牙开发中遇到的诸多问题,由于缺乏经验和能力相对不足,踩到了很多坑,不过在填坑过程中提高自身能力也是很满足的。