BLE开发的各种坑
这段时间在做低功耗蓝牙(BLE)应用的开发(并不涉及蓝牙协议栈)。总体感觉 Android BLE 还是不太稳定,开发起来也是各种痛苦。这里记录一些杂项和开发中遇到的问题及其解决方法,避免大家踩坑。本文说的问题有些没有得到官方文档的验证,不过也有一些论坛帖子的支持,也可以算是有一定根据。
Android 从 4.3(API Level 18) 开始支持低功耗蓝牙,但是只支持作为中心设备(Central)模式,这就意味着 Android 设备只能主动扫描和链接其他外围设备(Peripheral)。从 Android 5.0(API Level 21) 开始两种模式都支持。BLE 官方文档在 这里。
在
BluetoothAdapter.startLeScan()
的时候,在BluetoothAdapter.LeScanCallback.onLeScan()
中不能做太多事情,特别是周围的BLE设备多的时候,非常容易导致出现如下错误:E/GKI_LINUX(17741): ##### ERROR : GKI_exception: GKI_exception(): Task State Table
E/GKI_LINUX(17741): #####
E/GKI_LINUX(17741): ##### ERROR : GKI_exception: TASK ID [0] task name [BTU] state [1]
E/GKI_LINUX(17741): #####
E/GKI_LINUX(17741): ##### ERROR : GKI_exception: TASK ID [1] task name [BTIF] state [1]
E/GKI_LINUX(17741): #####
E/GKI_LINUX(17741): ##### ERROR : GKI_exception: TASK ID [2] task name [A2DP-MEDIA] state [1]
E/GKI_LINUX(17741): #####
E/GKI_LINUX(17741): ##### ERROR : GKI_exception: GKI_exception 65524 getbuf: out of buffers#####
E/GKI_LINUX(17741): ##### ERROR : GKI_exception:
E/GKI_LINUX(17741): **开发建议:在
onLeScan()
回调中只做尽量少的工作,可以把扫描到的设备,扔到另外一个线程中去处理,让onLeScan()
尽快返回。
[参考帖子]在使用
BluetoothDevice.connectGatt()
或者BluetoothGatt.connect()
等建立BluetoothGatt
连接的时候,在任何时刻都只能最多一个设备在尝试建立连接。如果同时对多个蓝牙设备发起建立 Gatt 连接请求。如果前面的设备连接失败了,后面的设备请求会被永远阻塞住,不会有任何连接回调。开发建议:如果要对多个设备发起连接请求,最好是有一个同一个的设备连接管理,把发起连接请求序列化起来。前一个设备请求建立连接,后面请求在队列中等待。如果连接成功了,就处理下一个连接请求。如果连接失败了(例如出错,或者连接超时失败),就马上调用
BluetoothGatt.disconnect()
来释放建立连接请求,然后处理下一个设备连接请求。
[参考帖子]对 BluetoothGatt 操作
(read/write)Characteristic()
,(read/write)Descriptor()
和readRemoteRssi()
都是异步操作。需要特别注意的是,同时只能有一个操作(有些贴这说只能同时有一个writeCharacteristic()
,这个我并没有严格验证),也就是等上一个操作回调(例如onCharacteristicWrite()
)以后,再进行下一个操作。开发建议:把这写操作都封装成同步操作,一个操作回调之前,阻塞主其他调用。
[参考帖子]BLE 设备的建立和断开连接的操作,例如
BluetoothDevice.connectGatt()
,BluetoothGatt.connect()
,BluetoothGatt.disconnect()
,BluetoothGatt.discoverServices()
等操作最好都放在主线程中,否则你会遇到很多意想不到的麻烦。开发建议:对
BluetoothGatt
的连接和断开请求,都通过发送消息到 Android 的主线程中,让主线程来执行具体的操作。例如创建一个new Handler(context.getMainLooper());
,把消息发送到这个Handler
中。
[参考帖子]如果你在开发 BLE 应用的时候,有时候会发现系统的功耗明显增加了,查看电量使用情况,蓝牙功耗占比非常高,好像低功耗是徒有虚名。使用
adb bugreport
获取的了系统信息,分析发现一个名叫BluetoothRemoteDevices
的WakeLock
锁持有时间非常长,导致系统进入不了休眠。分析源代码发现,在连接 BLE 设备的过程中,系统会持有 (Aquire)这个WakeLock
,直到连接上或者主动断开连接(调用disconnect()
)才会释放。如果BLE设备不在范围内,这个超时时间大约为30s,而这时你可能又要尝试重新连接,这个WakeLock
有被重新持有,这样系统就永远不能休眠了。开发建议:对BLE设备连接,连接过程要尽量短,如果连接不上,不要盲目进行重连,否这你的电池会很快被消耗掉。这个情况,实际上对传统蓝牙设备连接也是一样。
[参考帖子]Android 作为中心设备,最多只能同时连接 6 个 BLE 外围设备(可能不同的设备这个数字不一样),超过 6 个,就会连接不上了。现在 BLE 设备越来越多,其实并不够用,所以在开发的过程中,需要特别的谨慎使用。
开发建议:按照需要连接设备,如果设备使用完了,应该马上释放连接(调用
BluetoothGatt.close()
),腾出系统资源给其他可能的设备连接。
[参考帖子]发起蓝牙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
。[参考帖子]
本文只是一些经验之谈,观点也比较琐碎。这里很多问题都看起来是蓝牙协议栈不完善导致的,或许在后面 Android 升级中会修复这些问题,我这里说的可能不适用了。
BLE开发的各种坑的更多相关文章
- 【转】BLE开发的各种坑
原文网址:http://www.race604.com/android-ble-tips/ 这段时间在做低功耗蓝牙(BLE)应用的开发(并不涉及蓝牙协议栈).总体感觉 Android BLE 还是不太 ...
- 简述移动端IM开发的那些坑:架构设计、通信协议和客户端
1.前言 有过移动端开发经历的开发者都深有体会:移动端IM的开发,与传统PC端IM有很大的不同,尤其无线网络的不可靠性.移动端硬件设备资源的有限性等问题,导致一个完整的移动端IM架构设计和实现都充满着 ...
- 蓝牙4.0——Android BLE开发官方文档翻译
ble4.0开发整理资料_百度文库 http://wenku.baidu.com/link?url=ZYix8_obOT37JUQyFv-t9Y0Sv7SPCIfmc5QwjW-aifxA8WJ4iW ...
- 蓝牙4.0 BLE 开发
在BLE开发中的一些随记,供大家参考: 凡事皆有状态 低功耗蓝牙背后有个基本的概念:任何事务都有状态.状态可以是任何东西:当前的温度,设备里电池的状态,设备名称或者对测量温度的地点的描述.它通过属性服 ...
- iOS蓝牙BLE开发
蓝牙是一个标准的无线通讯协议,具有设备成本低.传输距离近和功耗低等特点,被广泛的应用在多种场合.蓝牙一般分为传统蓝牙和BLE两种模式:传统蓝牙可以传输音频等较大数据量,距离近.功耗相对大:而BLE则用 ...
- Android蓝牙BLE开发,扫描、连接、发送和读取信息;
1.BLE开发权限 Android蓝牙BLE开发须打开蓝牙权限和6.0位置权限: <uses-permission android:name="android.permission.B ...
- 快速接入 Android BLE 开发的基础框架
代码地址如下:http://www.demodashi.com/demo/12092.html ** Android BLE基础操作框架,基于回调,操作简单.包含扫描.多连接.广播包解析.服务读写及通 ...
- iOS开发遇到的坑之五--解决工程已存在plist表,数据却不能存入的问题
想写这篇博客其实在一两个月前开发遇见的时候就想把这个问题写成博客的,奈何自己一直懒外加一直没有时间,就把这个事情给耽搁了,好在当时知道下自己一定要把这个问题给描述出来,免得以后其他人遇到这个问题会纠结 ...
- Android使用WebView开发常见的坑
原文链接:http://mp.weixin.qq.com/s?__biz=MzAwODE1NTI2MQ==&tempkey=uP3a%2BOgIN7vPbLfJp3BTCl2KabYi1%2F ...
随机推荐
- java Date日期去掉时分秒
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String s = sdf.format(new Date() ...
- SCIP读书笔记(1)
这书也算是必修吧,尤其是我这种非科班人员,还是应该抽时间尽量学习一下.大致翻过一遍,习题非常多,尽力吧. ##构造过程抽象 * 为了表述认知,每种语言都提供了三种机制:基本元素:组合方式:抽象方法. ...
- 使用html5兼容低版本浏览器
因为html5 新出的一些语义化的标签,在低版本浏览器下不能识别,举个例子,比如你写了一个 header 标签中,写了一段文本,在低版本浏览器下,肯定是能看到的,但是,那是他是不认识 header标签 ...
- DataGrid列的合并
/// <summary> /// DataGrid列的合并 /// 注意:1.DataGrid在绑定的时候进行分组和排序,才能让相同的行放在一起 /// 2.方法应用的时机,应该在Dat ...
- js 配置基础启动文件
页面启动文件boot.js,获取存放该文件的路径,放置通用的css,js代码,方便html页面调用. __CreateJSPath = function (js) { var scripts = do ...
- maven依赖缺少oracle驱动包
maven添加oracle jdbc驱动的依赖问题 在maven工程里不能下载oracle的jdbc驱动jar包,需要Oracle官方授权才可以,找一个ojdbc14的10.2.0.1.0或10.2. ...
- SQL2005 学习笔记 窗口函数(OVER)【转】
1.简介: SQL Server 2005中的窗口函数帮助你迅速查看不同级别的聚合,通过它可以非常方便地累计总数.移动平均值.以及执行其它计算. 窗口函数功能非常强大,使用起来也十分容易.可以使用这个 ...
- Mysql 操作手册
mysql操作手册 版本:5.6.16mysql linux安装基本步骤:#rpm -e --nodeps mysql-lib-5.1.*#rpm -ivh mysql-server#rpm -ivh ...
- WinpCap 使用线程发数,明明发了,返回值0是OK的啊,怎么抓包看不到,难道不支持多线程。。。
if (!m_adapterHandle){ return false;}int rst = pcap_sendpacket((pcap_t*)m_adapterHandle,data ,dat ...
- Swift 中的getting和setter的使用
以下简单的介绍Swift中的getting和setting的使用方法: Xcode version: 6.1 先附代码: class Test { var num1: Double = 0.0 ini ...