BluetoothKit是一款功能强大的Android蓝牙通信框架,支持低功耗蓝牙设备的连接通信、蓝牙广播扫描及Beacon解析。

关于该项目的详细文档请关注:https://github.com/dingjikerbo/BluetoothKit

对于刚接触Android蓝牙开发的初学者来说,会经常遇到一些奇怪的坑,我也是一路走过来的,将我遇到的一些坑总结了一下,这些坑在这个项目中都修复了,所以大家不必再费时费力去重复踩一遍。这个项目目前正在不断更新,有什么更好的建议大家可以随时提出来。

蓝牙扫描相关的坑

一、startDiscovery在大多数手机上是可以同时发现经典蓝牙和Ble的,但是startDiscovery的回调无法返回Ble的广播,所以无法通过广播识别设备,且startDiscovery扫描Ble的效率比StartLeScan低很多。所以在实际应用中,还是StartDiscovery和StartLeScan分开扫,前者扫经典蓝牙,后者扫低功耗蓝牙。

二、startLeScan() 的时候,在onLeScan() 中不能做耗时操作,特别是周围的BLE设备多的时候,容易导致底层堵塞。如果有耗时操作请丢到子线程中去处理。

三、有的手机会过滤设备广播,一次扫描过程中只发一次request,如果没有收到response就再也收不到该设备广播回调了,解决的办法是多扫几次。

蓝牙连接相关的坑

一、对蓝牙设备的操作不能并行,只能串行,即每次都要在收到上一个操作的回调后才能继续下一个操作。但是断开连接例外,断开连接要马上closeGatt,不用等任务队列中的其他操作了。而且要给所有正在执行或者准备执行的任务都cancel。

二、有时候蓝牙协议栈出现异常可能收不到回调,所以我们要对每个操作做超时检查,否则后面的所有操作都被阻塞了。

三、对于超时的任务,最好closeGatt,下次重新连接的时候重开一个gatt。

四、蓝牙连接可能不稳定,最好支持失败自动重试机制,尤其是连接和发现服务,因为80%的问题都发生在建立连接和发现服务的时候,而且这一块也是最耗时的。

五、当连接建立后,可以由设备端发起更改连接间隔,这样能加快后续发现服务以及数据读写的速度。有的手机discover service很慢,原因是connect interval太大了,有的手机会主动向设备发起更改connect interval,而有的手机却不会。这样的话connect interval相差就会很大,实践中发现有的手机是7ms,有的手机是默认的50ms,所以发现service都要8s,甚至20s的都很寻常,这对用户来说是无法忍受的。所以比较好的办法是设备主动发起更改connect interval

六、同一个设备的所有操作最好都放在同一个线程串行执行,最好不要放在UI线程,虽然这些操作都是异步的,理论上来说不会耗时,但是由于涉及到跨进程,还是有可能出现ANR。另外不建议每个设备都开一个线程,设备多了会费内存也会降低性能。较好的做法是开一个线程,所有设备的操作都在该线程中发起,虽然占用同一个线程,但是每个设备各自维护自己的任务队列。

七、蓝牙操作都是异步的,回调通常都在binder线程里执行,因为这是跨进程回调回来的。一定要注意到这一点,否则会出现一些奇怪的问题。比如writeCharacter在工作线程,但是onCharacterWrite是在binder线程,回调里如果涉及到任务队列的调度一定要post回工作线程中处理,否则会出现多线程造成的数据不一致问题。

八、接着第七条,在回调中post回工作线程处理时要注意不要带句柄,而是要带数据。比如对于onCharacteristicWrite,不要带BluetoothGattCharacteristic,而是要带其中的value。否则会漏掉一些数据,且最后可能收到一组重复的数据。同样的问题在notify上也有。

九、当设备固件升级后,profile可能发生了变化,然而下次discover service的时候还是返回的旧的缓存,这样读写character可能会失败。解决办法是固件升级后,下次连接时刷新一下该设备的缓存。当然,重启蓝牙也会刷新缓存,不过会影响到所有设备。另外有时候discoverService服务发现的不全,或者根本发现不了服务,也可以考虑清除一下缓存。

十、有的手机上discoverService可能会回调不止一次onServiceDiscover,这个要注意防御。

十一、service不要缓存,虽然uuid什么的可能都没变,但是这些service都会和gatt关联的,如果gatt变了,那service就报废了,对这些service和character做任何读写操作都会出错。所以建议每次连接上时都去discover service,不要缓存。

十二、固件升级通常是写设备,为加快写速度,可以在write character时指定no response标志,实践发现速度可以提升2~3倍。不过要注意的是即便带了no response标志,也不代表这种写操作是没有回调的,我们仍然要遵循收到上一次写回调后才能进行下一次写操作。提升写速度的手段还有更改MTU和更改连接间隔,不过更改MTU硬件不一定支持,得尝试几次。

十三、写的时候不要指定writeType,不指定writeType不代表writeType就是WRITE_TYPE_DEFAULT,事实上系统会自动根据property来决定writeType,如果带PROPERTY_WRITE_NO_RESPONSE属性,则会自动选择WRITE_TYPE_NO_RESPONSE,否则才会选择WRITE_TYPE_DEFAULT。

十四、打开/关闭character的notify,必须等收到onDescriptorWrite回调之后才算结束,才能开始下一个任务。

十五、设备的gatt在不用时要及时关闭,系统支持的连接句柄数是有限的,当达到上限后无法再建立新的连接了。

十六、当连接断开后要调closeGatt释放资源,不用调disconnect,也不要下次复用之前的gatt来reconnect,因为有的手机上重连可能会存在问题,比如重连后死活发现不了service。这种情况下,最好只要断开连接就close gatt,下次连接时打开全新的gatt,这样就可以发现service了。

使用

使用起来非常简单,添加一行依赖,然后创建一个全局单例BluetoothClient,详细接口调用方法参考上面的链接。

1、在build.gradle中添加依赖

compile 'com.inuker.bluetooth:library:1.3.8'
  • 1
  • 1

2、创建一个BluetoothClient,最好做成单例全局使用

BluetoothClient mClient = new BluetoothClient(context);
  • 1
  • 1

使用到的一些技术

**一、实现了一个 
完整的跨进程异步任务队列,支持任务超时、出错重试及防御队列溢出**

二、拦截并Hook系统层蓝牙Binder,实现对所有蓝牙设备通信的监控,当同时连接设备数过多时会自动断掉活跃度最低的设备

三、整个框架封装在一个service中,可灵活指定service所在进程。

四、屏蔽了接口异步回调可能持有调用端Activity引用导致的内存泄露

五、利用动态代理自动将所有操作封闭在工作线程,所以整个框架无锁

Android BLE设备蓝牙通信框架BluetoothKit的更多相关文章

  1. Android BLE与终端通信(五)——Google API BLE4.0低功耗蓝牙文档解读之案例初探

    Android BLE与终端通信(五)--Google API BLE4.0低功耗蓝牙文档解读之案例初探 算下来很久没有写BLE的博文了,上家的技术都快忘记了,所以赶紧读了一遍Google的API顺便 ...

  2. Android BLE与终端通信(一)——Android Bluetooth基础API以及简单使用获取本地蓝牙名称地址

    Android BLE与终端通信(一)--Android Bluetooth基础API以及简单使用获取本地蓝牙名称地址 Hello,工作需要,也必须开始向BLE方向学习了,公司的核心技术就是BLE终端 ...

  3. 快速接入 Android BLE 开发的基础框架

    代码地址如下:http://www.demodashi.com/demo/12092.html ** Android BLE基础操作框架,基于回调,操作简单.包含扫描.多连接.广播包解析.服务读写及通 ...

  4. Android BLE与终端通信(四)——实现服务器与客户端即时通讯功能

    Android BLE与终端通信(四)--实现服务器与客户端即时通讯功能 前面几篇一直在讲一些基础,其实说实话,蓝牙主要为多的还是一些概念性的东西,当你把概念都熟悉了之后,你会很简单的就可以实现一些逻 ...

  5. Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信

    Android BLE与终端通信(三)--客户端与服务端通信过程以及实现数据通信 前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主 ...

  6. Android BLE与终端通信(二)——Android Bluetooth基础科普以及搜索蓝牙设备显示列表

    Android BLE与终端通信(二)--Android Bluetooth基础搜索蓝牙设备显示列表 摘要 第一篇算是个热身,这一片开始来写些硬菜了,这篇就是实际和蓝牙打交道了,所以要用到真机调试哟, ...

  7. Android BLE与终端通信(三)——client与服务端通信过程以及实现数据通信

    Android BLE与终端通信(三)--client与服务端通信过程以及实现数据通信 前面的终究仅仅是小知识点.上不了台面,也仅仅能算是起到一个科普的作用.而同步到实际的开发上去,今天就来延续前两篇 ...

  8. Android - 传统蓝牙通信聊天

    Android -传统蓝牙通信聊天 技术:java+Android4.4+jdk1.8 运行环境:Android4.4.Android7.0 概述 Android 传统蓝牙的使用,包括开关蓝牙.搜索设 ...

  9. Android -传统蓝牙通信聊天

    概述 Android 传统蓝牙的使用,包括开关蓝牙.搜索设备.蓝牙连接.通信等. 详细 代码下载:http://www.demodashi.com/demo/10676.html 原文地址: Andr ...

随机推荐

  1. PHP4个载入语句的区别

    4个载入语句的区别 include和require的区别: include载入文件失败时(即没有找到该文件),报一个“提示错误”,然后继续执行后续代码: requre载入文件失败时,报错并立即终止执行 ...

  2. SpringBoot(十四)_springboot使用内置定时任务Scheduled的使用(一)

    为什么使用定时? 日常工作中,经常会用到定时任务,比如各种统计,并不要求实时性.此时可以通过提前设置定时任务先把数据跑出来,后续处理起来更方便. 本篇文章主要介绍 springboot内置定时任务. ...

  3. 【跨域】jsonp跨域实现方法

    封装原生jsonp: 以跨域调取豆瓣电影最热榜单为例: function $jsonp(url,data,callback){ var funcName = 'jsonp_cb' + Math.ran ...

  4. TCP 协议连接与关闭的握手

     原文链接 http://blog.csdn.net/oney139/article/details/8103223   TCP头部: 其中 ACK   SYN  序号  这三个部分在以下会用到,它们 ...

  5. 【刷题】BZOJ 4543 [POI2014]Hotel加强版

    Description 同OJ3522 数据范围:n<=100000 Solution dp的设计见[刷题]BZOJ 3522 [Poi2014]Hotel 然后发现dp的第二维与深度有关,于是 ...

  6. BZOJ 3624: [Apio2008]免费道路

    3624: [Apio2008]免费道路 Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 1201  Solved:  ...

  7. 【BZOJ1019】[SHOI2008]汉诺塔(数论,搜索)

    [BZOJ1019][SHOI2008]汉诺塔(数论,搜索) 题面 BZOJ 洛谷 题解 首先汉诺塔问题的递推式我们大力猜想一下一定会是形如\(f_i=kf_{i-1}+b\)的形式. 这个鬼玩意不好 ...

  8. CF1027C Minimum Value Rectangle

    之前做的时候没想出来...现在来数学推导一波. 题意:从n个木棒中选出4个拼成一个矩形,使得 (周长)2/面积 最小. 解:设矩形宽a长b.我们要最小化下面这个式子: 去掉常数,不妨设b = a + ...

  9. 使用solrJ操作solr常用方法 【注释非常详细,非常好】

    转: 使用solrJ操作solr常用方法 2017年08月07日 22:49:06 成都往右 阅读数:8990   版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.cs ...

  10. NodeJS 笔记 path模块

    path 模块,本模块包含一系列处理和转换文件路径的工具集. path.normalize(path)   normalize函数将不符合规范的路径经过格式化转换为标准路径,解析路径中的.与..外,还 ...