Android BLE设备蓝牙通信框架BluetoothKit
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的更多相关文章
- Android BLE与终端通信(五)——Google API BLE4.0低功耗蓝牙文档解读之案例初探
Android BLE与终端通信(五)--Google API BLE4.0低功耗蓝牙文档解读之案例初探 算下来很久没有写BLE的博文了,上家的技术都快忘记了,所以赶紧读了一遍Google的API顺便 ...
- Android BLE与终端通信(一)——Android Bluetooth基础API以及简单使用获取本地蓝牙名称地址
Android BLE与终端通信(一)--Android Bluetooth基础API以及简单使用获取本地蓝牙名称地址 Hello,工作需要,也必须开始向BLE方向学习了,公司的核心技术就是BLE终端 ...
- 快速接入 Android BLE 开发的基础框架
代码地址如下:http://www.demodashi.com/demo/12092.html ** Android BLE基础操作框架,基于回调,操作简单.包含扫描.多连接.广播包解析.服务读写及通 ...
- Android BLE与终端通信(四)——实现服务器与客户端即时通讯功能
Android BLE与终端通信(四)--实现服务器与客户端即时通讯功能 前面几篇一直在讲一些基础,其实说实话,蓝牙主要为多的还是一些概念性的东西,当你把概念都熟悉了之后,你会很简单的就可以实现一些逻 ...
- Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信
Android BLE与终端通信(三)--客户端与服务端通信过程以及实现数据通信 前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主 ...
- Android BLE与终端通信(二)——Android Bluetooth基础科普以及搜索蓝牙设备显示列表
Android BLE与终端通信(二)--Android Bluetooth基础搜索蓝牙设备显示列表 摘要 第一篇算是个热身,这一片开始来写些硬菜了,这篇就是实际和蓝牙打交道了,所以要用到真机调试哟, ...
- Android BLE与终端通信(三)——client与服务端通信过程以及实现数据通信
Android BLE与终端通信(三)--client与服务端通信过程以及实现数据通信 前面的终究仅仅是小知识点.上不了台面,也仅仅能算是起到一个科普的作用.而同步到实际的开发上去,今天就来延续前两篇 ...
- Android - 传统蓝牙通信聊天
Android -传统蓝牙通信聊天 技术:java+Android4.4+jdk1.8 运行环境:Android4.4.Android7.0 概述 Android 传统蓝牙的使用,包括开关蓝牙.搜索设 ...
- Android -传统蓝牙通信聊天
概述 Android 传统蓝牙的使用,包括开关蓝牙.搜索设备.蓝牙连接.通信等. 详细 代码下载:http://www.demodashi.com/demo/10676.html 原文地址: Andr ...
随机推荐
- jdk命令行工具:jstat与jmap
转自文章:http://blog.csdn.net/gzh0222/article/details/8538727 C:\Users\Administrator\Desktop>jstat -g ...
- bootstrap响应式布局列子
<!DOCTYPE html><html lang="zh-CN"> <head> <meta charset="utf-8&q ...
- 从0在windows上一次性上传本地整个项目(包含所有文件/文件夹)到 Github
1.注册并登陆Github. 2.登陆进去之后的页面,点击这个“库”,这表示你在Github上上的代码仓库,我这里已经创建过一个了,所以数量是1 3.在仓库选项卡中,点击“新建”按钮添加一个项目. 4 ...
- java学习二 数据类型自动提升 0x开头的数据是 16进制且是int型
变量只能定义一次,不能定义两次, 变量的作用域:当前的大括号与子括号才有效 变量的作用:存储值,取值 整型:向上自动升级,向下强制降级 char,byte,shot参与运算时候自动提升为int型 因为 ...
- java通过dom读写xml文件
java通过dom读写xml文件 要读的xml文件 <?xml version="1.0" encoding="GB2312"?><学生花名册 ...
- LOJ#2983. 「WC2019」数树
传送门 抄题解 \(Task0\),随便做一下,设 \(cnt\) 为相同的边的个数,输出 \(y^{n-cnt}\) \(Task1\),给定其中一棵树 设初始答案为 \(y^n\),首先可以发现, ...
- 《剑指offer》— JavaScript(33)丑数
丑数 题目描述 把只包含质因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 思 ...
- Java 泛型类型基础
为什么要使用泛型? 未使用泛型的情况: // 创建列表类 List list = new ArrayList(); // 添加一个类型为 String 的列表元素 list.add("hel ...
- SQL Server 数据库对象命名参考
一. 引言 编码规范是一个优秀程序员的必备素质,然而,有很多人非常注重程序中变量.方法.类的命名,却忽视了同样重要的数据库对象命名.这篇文章结合许多技术文章和资料,以及我自己的开发经验,对数据库对象的 ...
- java基础-多线程应用案例展示
java基础-多线程应用案例展示 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.两只熊,100只蜜蜂,蜜蜂每次生产的蜂蜜量是1,罐子的容量是30,熊在罐子的蜂蜜量达到20的时候 ...