Android Ble

蓝牙的历史?

1999年5月20日,索尼爱立信、IBM、英特尔、诺基亚及东芝等业界龙头创立蓝牙特别兴趣组(SIG,Special Interest Group),制订蓝牙技术标准。“蓝牙”(Bluetooth)这名称来自10世纪的丹麦国王哈拉尔德(Harald Gormsson)的外号。出身海盗家庭的哈拉尔德统一了北欧四分五裂的国家,成为维京王国的国王。由于他喜欢吃蓝莓,牙齿常常被染成蓝色,而获得“蓝 牙”的绰号,当时蓝莓因为颜色怪异的缘故被认为是不适合食用的东西,因此这位爱尝新的国王也成为创新与勇于尝试的象征。1998年,爱立信公司希望无线通信技术能统一标准而取名“蓝芽”。蓝牙,或称为蓝芽,是一种无线个人局域网(Wireless PAN),最初由爱立信创制,后来由蓝牙技术联盟订定技术标准。据说因为此技术尚在萌芽的阶段,故将Bluetooth以“蓝芽”的中文译名在台湾进行商 业的注册,不过在2006年,该组织已将全球中文统一为“蓝牙”。

什么是Ble?

BLE 是Bluetooth Low Energy的缩写,又叫蓝牙4.0,区别于蓝牙3.0和之前的技术。BLE前身是NOKIA开发的Wibree技术,主要用于实现移动智能终端与周边配件之间的持续连接,是功耗极低的短距离无线通信技术,并且有效传输距离被提升到了100米以上,同时只需要一颗纽扣电池就可以工作数年之久。BLE是在蓝牙技术的基础上发展起来的,既同于蓝牙,又区别于传统蓝牙。BLE设备分单模和双模两种,双模简称BR,商标为Bluetooth Smart Ready,单模简称BLE或者LE,商标为Bluetooth Smart。Android是在4.3后才支持BLE,这可以解释不是所有蓝牙手机都支持BLE,而且支持BLE的蓝牙手机一般是双模的。


Ble的工作原理?

大概知道ble是什么东西后,我们就来了解下他的工作原理。想要了解原理,先搞清楚蓝牙通信之间的关系——主从关系

蓝牙技术规定每一对设备之间进行蓝牙通讯时,必须一个为主角色,另一为从角色,才能进行通信,通信时,必须由主端进行查找,发起配对,建链成功后,双方即可收发数据。理论上,一个蓝牙主端设备,可同时与7个蓝牙从端设备进行通讯。一个蓝牙设备以主模式发起呼叫时,需要知道对方的蓝牙地址,配对密码等信息,配对完成后,可直接发起呼叫。这可以解释为什么有时无法连接蓝牙,有可能是连接的蓝牙设备过多。

蓝牙内部详细的工作原理呢,由于个人能力有限就不详细讲述了,本系列文章主要是介绍下ble在android中的开发。


Ble在android交互中的角色与职责:

Android设备与BLE设备交互有两组角色:就是中心设备与周边设备了。如图所示:

Ble开发的大概原理流程:

Android ble蓝牙问题

 

(1)蓝牙回调
安卓4.4的蓝牙回调是在异步线程中(不在主线程),若要在蓝牙回调中执行更新界面的操作,记得切换到主线程去操作

 

 

(2)三星手机兼容性问题
connectGatt()方法在某些三星手机上只能在UI线程调用。

备注:三星的手机是connet和disconnet还有connectGatt都要在UI线程中操作

 

 

(3)Android L 新API
Android L换了一套扫描设备的API:BluetoothLeScanner.startScan(List, ScanSettings, ScanCallback)

 

 

(4)Android M新的权限(android 6.0 动态权限)
Android M中必须拥有定位权限才能扫描BLE设备

 

(4)连接不断开的问题
别的BLE程序非法保留连接的设备可能会导致连接不能断开

 

(5)异步问题
读写Characteristic、Descriptor等几乎所有BLE操作结果都为异步返回,若不等待上一次操作结果返回就执行下一次操作,很可能导致操作失败或者操作无效。onDescriptorWrite()返回的线程与写入线程为同一个线程,别的操作一般在不同的线程回调。

 

 

(6)设备缓存
Android会对连接过的BLE设备的Services进行缓存,若设备升级后Services等有改动,则程序会出现通讯失败。此时就得刷新缓存,但是刷新缓存的方法并没有开放,这里只能使用反射来调用BluetoothGatt类中的refresh()方法:

复制代码
1 try {
2   Method localMethod = mBluetoothGatt.getClass().getMethod("refresh");
3   if (localMethod != null) {
4       return (Boolean) localMethod.invoke(mBluetoothGatt);
5   }
6 } catch (Exception localException) {
7   Log.e("refreshServices()", "An exception occured while refreshing device");
8 }
复制代码

 

(7)扫描设备
startLeScan(UUID[], BluetoothAdapter.LeScanCallback)

在Android4.4及以下手机中似乎只支持16位的短UUID,不支持128位完整的UUID。

(9)任何出错,超时,用完就马上调用Gatt.disconnect(), Gatt.close()。

(10)从bindService 到 onServiceConnected 这个回调花费时间较长, onServiceConnected 这个回调很可能在 MainActivity onResume之后才执行, 所以不要指望onResume里去执行扫描,因为此时serviceConnected 回调都尚未执行

(11)getBtAdapter().enable()是异步,立即返回,但从 off 到 on 的过程需要一个时间所以只能监听系统broadcast发出的intent里的state

(12) 多次扫描蓝牙,在华为荣耀,魅族M3 NOTE 中有的机型,会发现多次断开–扫描–断开–扫描… 会扫描不到设备,此时需要在断开连接后,不能立即扫描,而是要先停止扫描后,过2秒再扫描才能扫描到设备。

(13)扫描尽量不要放在主线程进行,可以放入子线程里。不然有些机型会出现 do too many work in main thread.

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

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

(16)BLE的特征一次读写最大长度20字节。

 (17)一个主设备(例如Android手机)可以同时连接多个从设备(一般为6个,例如智能硬件。超过就连接不上了),一个从设备只能被一个主设备连接,一旦从设备连接上主设备,就停止广播,断开连接则继续广播。在任何时刻都只能最多一个设备在尝试建立连接。如果同时对多个蓝牙设备发起建立Gatt连接请求。如果前面的设备连接失败了,则后面的设备请求会被永远阻塞住,不会有任何连接回调。所以建议:如果要对多个设备发起连接请求,最好是一个接一个的顺序同步请求管理。

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

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

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

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

(22)Android 从 4.3(API Level 18) 开始支持低功耗蓝牙,但是只支持作为中心设备 (Central) 模式,这就意味着 Android 设备只能主动扫描和链接其他外围设备 (Peripheral)。从Android 5.0(API Level 21)开始两种模式都支持。BLE 官方文档在  这里 。 

(23)

在  BluetoothAdapter.startLeScan() 的时候,在 BluetoothAdapter.LeScanCallback.onLeScan() 中不能做太多事情,特别是周围的BLE设备多的时候

开发建议:在  onLeScan() 回调中只做尽量少的工作,可以把扫描到的设备,扔到另外一个线程中去处理,让  onLeScan() 尽快返回。 

(24)BLE 设备的建立和断开连接的操作,例如 BluetoothDevice.connectGatt() ,  BluetoothGatt.connect() , BluetoothGatt.disconnect() 等操作最好都放在主线程中,否则你会遇到很多意想不到的麻烦。 

开发建议:对  BluetoothGatt 的连接和断开请求,都通过发送消息到 Android 的主线程中,让主线程来执行具体的操作。例如创建一个  new Handler(context.getMainLooper()); ,把消息发送到这个  Handler中。 

 (25)

如果你在开发 BLE 应用的时候,有时候会发现系统的功耗明显增加了,查看电量使用情况,蓝牙功耗占比非常高,好像低功耗是徒有虚名。使用  adb bugreport 获取的了系统信息,分析发现一个名叫 BluetoothRemoteDevices 的  WakeLock 锁持有时间非常长,导致系统进入不了休眠。分析源代码发现,在连接 BLE 设备的过程中,系统会持有 (Aquire) 这个  WakeLock ,直到连接上或者主动断开连接(调用  disconnect() )才会释放。如果BLE设备不在范围内,这个超时时间大约为30s,而这时你可能又要尝试重新连接,这个  WakeLock 有被重新持有,这样系统就永远不能休眠了。 

开发建议:对BLE设备连接,连接过程要尽量短,如果连接不上,不要盲目进行重连,否这你的电池会很快被消耗掉。这个情况,实际上对传统蓝牙设备连接也是一样。  [  参考帖子 ] 

(26)

Android 作为中心设备,最多只能同时连接 6 个 BLE 外围设备(可能不同的设备这个数字不一样),超过 6 个,就会连接不上了。现在 BLE 设备越来越多,其实并不够用,所以在开发的过程中,需要特别的谨慎使用。

开发建议:按照需要连接设备,如果设备使用完了,应该马上释放连接(调用 BluetoothGatt.close() ),腾出系统资源给其他可能的设备连接。  













你可能感兴趣的:(Android)