Android BLE开发的各种坑

发布时间:2020-07-10 00:44:25 作者:xinyu1993abc
来源:网络 阅读:2321

这段时间在做低功耗蓝牙(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/GKILINUX(17741): ##### ERROR : GKIexception: GKIexception(): Task State Table E/GKILINUX(17741): ##### 
E/GKILINUX(17741): ##### ERROR : GKIexception: TASK ID [0] task name [BTU] state [1] 
E/GKILINUX(17741): ##### 
E/GKI
LINUX(17741): ##### ERROR : GKIexception: TASK ID [1] task name [BTIF] state [1] 
E/GKI
LINUX(17741): ##### 
E/GKILINUX(17741): ##### ERROR : GKIexception: TASK ID [2] task name [A2DP-MEDIA] state [1] 
E/GKILINUX(17741): ##### 
E/GKI
LINUX(17741): ##### ERROR : GKIexception: GKIexception 65524 getbuf: out of buffers##### 
E/GKILINUX(17741): ##### ERROR : GKIexception: 
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.discoverServices()等操作最好都放在主线程中,否则你会遇到很多意想不到的麻烦。

开发建议:对 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()),腾出系统资源给其他可能的设备连接。

8、发起蓝牙Gatt连接 BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback),这里有一个参数autoConnect,如果为 true 的话,系统就会发起一个后台连接,等到系统发现了一个设备,就会自动连上,通常这个过程是非常慢的。为 false 的话,就会直接连接,通常会比较快。同样,BluetoothGatt.connect()只能发起一个后台连接,不是直接连接。所以这个地方需要小心。

public boolean connect() {
    try {
        mService.clientConnect(mClientIf, mDevice.getAddress(),
                               false, mTransport); // autoConnect is inverse of "isDirect"
        return true;
    } catch (RemoteException e) {
        Log.e(TAG,"",e);
        return false;
    }
}


开发建议:如果你需要快速连接(通常情况下我们都希望这样),在 connectGatt()的时候,传入 autoConnect=false 的参数。如果需要调用 BluetoothGatt.connect() 连接,可一通过反射的方式,强制 mService.clientConnect() 发起直接连接,也就是传入参数 isDirect=true。









推荐阅读:
  1. 入坑 Android Gradle 插件开发(一)
  2. Android源码个个击破之应用蓝牙扫描界面

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

蓝牙 error android

上一篇:顺序表的基本操作——增删查改

下一篇:phpcms网站前台页面访问正常,但是输入后台地址后,一片空白,没有任何错误提示

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》