BluetoothKit是一款功能强大的Android蓝牙通讯框架,支持低功耗蓝牙设备的链接通讯、蓝牙广播扫描及Beacon解析。android
关于该项目的详细文档请关注:https://github.com/dingjikerbo/BluetoothKitgit
对于刚接触Android蓝牙开发的初学者来讲,会常常遇到一些奇怪的坑,我也是一路走过来的,将我遇到的一些坑总结了一下,这些坑在这个项目中都修复了,因此你们没必要再费时费力去重复踩一遍。这个项目目前正在不断更新,有什么更好的建议你们能够随时提出来。github
1、startDiscovery在大多数手机上是能够同时发现经典蓝牙和Ble的,可是startDiscovery的回调没法返回Ble的广播,因此没法经过广播识别设备,且startDiscovery扫描Ble的效率比StartLeScan低不少。因此在实际应用中,仍是StartDiscovery和StartLeScan分开扫,前者扫经典蓝牙,后者扫低功耗蓝牙。缓存
2、startLeScan() 的时候,在onLeScan() 中不能作耗时操做,特别是周围的BLE设备多的时候,容易致使底层堵塞。若是有耗时操做请丢到子线程中去处理。多线程
3、有的手机会过滤设备广播,一次扫描过程当中只发一次request,若是没有收到response就再也收不到该设备广播回调了,解决的办法是多扫几回。框架
1、对蓝牙设备的操做不能并行,只能串行,即每次都要在收到上一个操做的回调后才能继续下一个操做。可是断开链接例外,断开链接要立刻closeGatt,不用等任务队列中的其余操做了。并且要给全部正在执行或者准备执行的任务都cancel。异步
2、有时候蓝牙协议栈出现异常可能收不到回调,因此咱们要对每一个操做作超时检查,不然后面的全部操做都被阻塞了。post
3、对于超时的任务,最好closeGatt,下次从新链接的时候重开一个gatt。性能
4、蓝牙链接可能不稳定,最好支持失败自动重试机制,尤为是链接和发现服务,由于80%的问题都发生在创建链接和发现服务的时候,并且这一块也是最耗时的。gradle
5、当链接创建后,能够由设备端发起更改链接间隔,这样能加快后续发现服务以及数据读写的速度。有的手机discover service很慢,缘由是connect interval太大了,有的手机会主动向设备发起更改connect interval,而有的手机却不会。这样的话connect interval相差就会很大,实践中发现有的手机是7ms,有的手机是默认的50ms,因此发现service都要8s,甚至20s的都很寻常,这对用户来讲是没法忍受的。因此比较好的办法是设备主动发起更改connect interval
6、同一个设备的全部操做最好都放在同一个线程串行执行,最好不要放在UI线程,虽然这些操做都是异步的,理论上来讲不会耗时,可是因为涉及到跨进程,仍是有可能出现ANR。另外不建议每一个设备都开一个线程,设备多了会费内存也会下降性能。较好的作法是开一个线程,全部设备的操做都在该线程中发起,虽然占用同一个线程,可是每一个设备各自维护本身的任务队列。
7、蓝牙操做都是异步的,回调一般都在binder线程里执行,由于这是跨进程回调回来的。必定要注意到这一点,不然会出现一些奇怪的问题。好比writeCharacter在工做线程,可是onCharacterWrite是在binder线程,回调里若是涉及到任务队列的调度必定要post回工做线程中处理,不然会出现多线程形成的数据不一致问题。
8、接着第七条,在回调中post回工做线程处理时要注意不要带句柄,而是要带数据。好比对于onCharacteristicWrite,不要带BluetoothGattCharacteristic,而是要带其中的value。不然会漏掉一些数据,且最后可能收到一组重复的数据。一样的问题在notify上也有。
9、当设备固件升级后,profile可能发生了变化,然而下次discover service的时候仍是返回的旧的缓存,这样读写character可能会失败。解决办法是固件升级后,下次链接时刷新一下该设备的缓存。固然,重启蓝牙也会刷新缓存,不过会影响到全部设备。另外有时候discoverService服务发现的不全,或者根本发现不了服务,也能够考虑清除一下缓存。
10、有的手机上discoverService可能会回调不止一次onServiceDiscover,这个要注意防护。
11、service不要缓存,虽然uuid什么的可能都没变,可是这些service都会和gatt关联的,若是gatt变了,那service就报废了,对这些service和character作任何读写操做都会出错。因此建议每次链接上时都去discover service,不要缓存。
12、固件升级一般是写设备,为加快写速度,能够在write character时指定no response标志,实践发现速度能够提高2~3倍。不过要注意的是即使带了no response标志,也不表明这种写操做是没有回调的,咱们仍然要遵循收到上一次写回调后才能进行下一次写操做。提高写速度的手段还有更改MTU和更改链接间隔,不过更改MTU硬件不必定支持,得尝试几回。
十3、写的时候不要指定writeType,不指定writeType不表明writeType就是WRITE_TYPE_DEFAULT,事实上系统会自动根据property来决定writeType,若是带PROPERTY_WRITE_NO_RESPONSE属性,则会自动选择WRITE_TYPE_NO_RESPONSE,不然才会选择WRITE_TYPE_DEFAULT。
十4、打开/关闭character的notify,必须等收到onDescriptorWrite回调以后才算结束,才能开始下一个任务。
十5、设备的gatt在不用时要及时关闭,系统支持的链接句柄数是有限的,当达到上限后没法再创建新的链接了。
十6、当链接断开后要调closeGatt释放资源,不用调disconnect,也不要下次复用以前的gatt来reconnect,由于有的手机上重连可能会存在问题,好比重连后死活发现不了service。这种状况下,最好只要断开链接就close gatt,下次链接时打开全新的gatt,这样就能够发现service了。
使用起来很是简单,添加一行依赖,而后建立一个全局单例BluetoothClient,详细接口调用方法参考上面的连接。
一、在build.gradle中添加依赖
compile 'com.inuker.bluetooth:library:1.3.8'
二、建立一个BluetoothClient,最好作成单例全局使用
BluetoothClient mClient = new BluetoothClient(context);
**1、实现了一个
完整的跨进程异步任务队列,支持任务超时、出错重试及防护队列溢出**
2、拦截并Hook系统层蓝牙Binder,实现对全部蓝牙设备通讯的监控,当同时链接设备数过多时会自动断掉活跃度最低的设备
3、整个框架封装在一个service中,可灵活指定service所在进程。
4、屏蔽了接口异步回调可能持有调用端Activity引用致使的内存泄露
5、利用动态代理自动将全部操做封闭在工做线程,因此整个框架无锁