Android 低功耗蓝牙BLE 开发注意事项

基本概念和问题

1、蓝牙设计范式?

当手机通过扫描低功耗蓝牙设备并连接上后,手机与蓝牙设备构成了客户端-服务端架构。手机通过连接蓝牙设备,可以读取蓝牙设备上的信息。手机就是客户端,蓝牙设备是服务端。

手机做为客户端可以连接多个蓝牙设备,所以手机又可以叫中心设备(Central),蓝牙设备叫外围设备(Peripheral)。

还有另外一个称谓:手机叫主设备(Master),蓝牙设备叫从设备(Slave)。

Android4.3 开始支持低功耗蓝牙,此版本只支持单模式:同时只能工作在中心设备模式或者外围设备模式

Android5.0 开始支持主从一体。换句话说,手机可以扫描并进行连接,连接着蓝牙设备的同时,又可以作为广播者,发送蓝牙广播,等待别的支持蓝牙扫描的设备连接自己。

2、从设备连接数量的问题?

理论层面

从经典蓝牙时代开始,蓝牙有个星型拓扑的概念,一个主设备(Central)外围有七个从设备(Peripheral),蓝牙核心文档规定了:同一时间只允许七个从设备进行连接。

系统层面

Android系统蓝牙协议栈源码中也使用了这个数值,Android手机的蓝牙芯片都是双模蓝牙芯片,即同时支持经典蓝牙和低功耗蓝牙,分析过协议栈源码,建立连接的过程经典蓝牙和低功耗蓝牙是公用的代码,所以手机作为主设备(Central)时,从设备(Peripheral)同时连接的最大值就是7台设备。

实际情况

开发Android客户端以来,遇到的实际情况就是,部分手机(偏低端一些机型,比如采用联发科的解决方案,手机的GPS、蓝牙、Wi-Fi等都是共模的,都集成在一个芯片上)不能达到7台设备。

3、ATT是什么?

ATT是属性协议(Attribute Protocol),定义了客户端与服务器如何相互发送符合标准的消息。

4、GATT是什么?

GATT是通用属性规范(Generic Attribute Profile),定义了如何发现与使用服务、特性与描述符的标准方法。

GATT的规程基本分为:

发现规程:发现服务(Service)、发现特征(Characteristic)等

客户端发起规程:读取特征(readCharacteristic)、写入特征(writeCharacteristic)等

服务端发起规程:比如通知(Notification)和指示(Indicate)

5、低功耗蓝牙频段和信道问题

蓝牙工作在2.45G ISM频段,波段范围是:2400-2483.5 MHz

信道:低功耗蓝牙使用用40个RF信道,这些RF信道中心频率为:f=2402+k*2 MHz, k=0, ... ,39

因为调试指数放宽,低功耗蓝牙的信道与经典蓝牙有所不同。每个信道的功率谱更宽,因此,为了避免邻近信道干扰,低功耗蓝牙的信道宽度为2MHz,而不是经典蓝牙的1MHz

低功耗蓝牙使用的2.45GHz频段已经非常拥挤,仅仅考虑标准的技术就包括:经典蓝牙、低功耗蓝牙、IEEE 802.11、IEEE802.11b、IEEE802.11g、IEEE802.11n以及IEEE 802.15.4。另外,许多私有的无线电同样使用这个频段,包括X10视频中继器、无线报警、键盘和鼠标等。许多其他设备也会在该频段发射噪声,例如街灯和微波炉。

对于2.45G这个频段有个很尴尬的特性:怕水。

举个例子:微波炉的工作原理就是向带有水分的物体发射2.45GHz的微波,利用了水分子能够很好的吸收2.45GHz电磁波,将电磁波能量转换成为自身的热量。也正式这个特性,在很长一段时间里,2.4GHz信道不被人所重视,下雨、雾气甚至是潮湿的墙壁都能吸收无线电波,使传输距离大大衰减。估计这也是全球都对此频段不屑而免费开放的理由之一吧。当人站在两块蓝牙设备中间,并且距离其中一块模块1米左右时,能够检测信号衰减了将近10dB左右!因为人体的70%左右是水分。

蓝牙链路层信道映射图:数据信道:0-36;广播信道:37、38、39

有人也许好奇为啥广播信道这么设计,请看下图:

链路层信道与Wi-Fi信道共存

是为了尽量避开冲突频段,增加通信的鲁棒性。

6、关于autoConnect参数为true的意义?

在蓝牙核心文档Vol3: Core System Package[Host volume]->Part C: Generic Access Profile的Connection Modes and Procedures章节中有涉及到自动连接建立规程(Auto Connection Establishment Procedure)的定义。

自动连接建立规程用来向多个设备同时发起连接。一个中央设备的主机与多个外围设备绑定,只要它们开始广播,便立刻与其建立连接。跟多细节请参考蓝牙核心文档和协议栈源码。

一些API使用问题

Android 4.3

此版本是首个支持BLE的Android版本,稳定性一般,现在的系统分布情况,基本可以把最低支持版本提高的Android4.4了

Android 5.0

Samsung手机出现BluetoothAdapter.startLeScan()方法使用不当导致的Crash

正常调用过程startLeScan() -> stopLeScan() -> startLeScan() -> stopLeScan(),不会出现Crash

异常调用startLeScan() -> startLeScan()会出现Crash

Android 6.0

在Android 6.0版本,需要APP获取位置权限才可以使用蓝牙API,部分机型在未授权时,调用蓝牙API会引起Crash

Android 6.0.1

Android6.0.1有个连接问题,是系统bug,影响连接问题。

Android 7.0

30s内连续扫描次数不允许大于5次,否则会引起无法扫描到设备的问题,需要重启才可以恢复正常。

并发执行BluetoothGatt.readRemoterssi()会引发DeadObjectException,三星手机出现概率较高。

Android 8.1 

扫描方法BluetoothAdapter.startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback);

部分手机如果没有指定serviceuuids值,手机锁屏后,扫描回调会失败,无法扫描到设备。Google亲儿子Pixel系列必现。

待更新......

另外一些普遍问题

BluetoothDevice.getName() 获取名字是有些不可靠的,因为有些情况下获取Name是空,慎用此方法获取的值来作为扫描过滤条件。

BLE设备的建立和断开连接的操作,最好都放在主线程中:例如BluetoothDevice.connectGatt(),BluetoothGatt.connect(),BluetoothGatt.disconnect(),BluetoothGatt.discoverServices()等

BLE 应用异常耗电问题,在连接 BLE 设备的过程中,系统会持有这个 WakeLock,直到连接上或者主动断开连接才会释放。如果BLE设备不在范围内,这个超时时间大约为30s,而这时你可能又要尝试重新连接,这个WakeLock又被重新持有,这样系统就永远不能休眠了。

Android BLE蓝牙的各种问题,只要做到如下几点,大部分问题会得到解决:

原则一:startLeScan()和stopLeScan()一定要确保成对出现、顺序调用。

否则会导致协议栈中mClientIf达到上限,扫描registerClient失败,再也不能扫描到设备,此时onScanFailed会发生errorCode=2。

原则二:BluetoothDevice.connectGatt()、BluetoothGatt.disconnect()和BluetoothGatt.close()一定要顺序调用。一些情况下可以直接越过disconnect()方法直接调用close()。

close()非常重要,对于一个执行过连接方法的设备,不管是否连接成功,最后都要调用close(),让系统底层回收掉资源,否则会有各种问题让你崩溃。

对于连接成功的蓝牙设备,想断开时,可以先调用BluetoothGatt.disconnect(),等待onConnectionStateChange响应断开后,再执行close()。

如果是执行连接方法时出现了无法恢复的错误,比如133、8、19、22、62等,可以直接调用close()。

原则三:读/写特征和描述、设置通知和指示等操作,要确保上一个执行完成了,再执行下一个调用。

Android源码中使用了mDeviceBusy全局变量,同时调用两个API,会导致后调用的直接失败。

原则四:由于蓝牙指令执行出现异常的概率还是比较高的,所以保证每次蓝牙操作的互斥性很必要、还有就是执行异常发生时,需要进行必要的重试操作,经过几个月对上线版本的监测,适当的重试对降低执行出错率改善明显。

附录:API常见错误码

GATT_ERROR    0x85    //133任何不惧名字的错误都出现这个错误码,出现了就认怂吧,重新连接吧。

GATT_CONN_TIMEOUT    0x08    //8  连接超时,大多数情况是设备离开可连接范围,然后手机端连接超时断开返回此错误码。

GATT_CONN_TERMINATE_PEER_USER     0x13    //19  连接被对端设备终止,直白点就是手机去连接外围设备,外围设备任性不让连接执行了断开。

GATT_CONN_TERMINATE_LOCAL_HOST    0x16    //22  连接被本地主机终止,可以解释为手机连接外围设备,但是连接过程中出现一些比如鉴权等问题,无法继续保持连接,主动执行了断开操作。

GATT_CONN_FAIL_ESTABLISH      03E    //62  连接建立失败。

避坑指南

基于Android蓝牙接口封装了一个库,将一些会引发错误的地方规避掉了,以后再开发相关应用,可以省一些心力,少掉一些头发😂😂😂。

项目特性:

1、封装了蓝牙扫描API,设置扫描模式非常方便,支持省电模式、前后台扫描策略、扫描过滤等。还是支持周期扫描模式。

2、封装了Notification、Read、Write、ReadRssi等常用操作,支持立即执行和并发&缓存两类API。立即执行类API支持执行超时设置。并发&缓存类API,会判断当前是否有正在执行的蓝牙操作,自动缓存延迟执行蓝牙指令,有效降低了多设备连接时,指令执行异常发生的概率。

3、支持连接异常重试操作,有效降低连接异常(比如GATT_ERROR等)导致的连接失败概率。

项目地址:https://github.com/bingerz/flip-ble

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342