Android蓝牙4.0 BLE开发坑总结

  1. onServicesDiscovered 回调里不能直接执行 write /readDataFromCharacteristic() 或者 enableNotificationOfCharacteristic之类的,而要放到主线程里执行,如 handler.post( … );web

  2. 若是发现链接上了,service也discover到了,可是始终不能触发onCharacteristicChanged的,必定要查找以下2个重要缘由:
    1). 必定要gatt.setCharacteristicNotification(characteristic, enable);
    2). 若是设置了1).却仍是发现没有触发,这个时候比较坑爹了,加上对此Characteristic的descriptor作indication Enable就应该能够了;缓存

for(BluetoothGattDescriptor dp:characteristic().getDescriptors()) {
dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt().writeDescriptor(dp);
}异步

3.. 不一样的机型的discoverService到onServiceDiscovered之间的耗时长短不一,这会致使一个问题:若是蓝牙硬件设备支持离线传输,即有记忆功能,链接上以后多久发送以前的数据的问题。若是链接之上当即发送,那么手机端的onServiceDiscovered还没有触发,这样Characteristic的值就获取不了(由于你的service,Characteristic都还没有初始化好),从而致使失败。svg

解决的办法有3个:
1)创建一套ACK机制,蓝牙硬件设备不断的广播,直到全部的数据都收到返回的ACK确认才再也不广播便可。
2)更好的办法是,当手机端onServiceDiscovered触发后,而且service,Characteristic都初始化好后,发送指令给蓝牙硬件设备(即writeCharacteristic)表示手机端已经准备好,能够发送数据给我了,蓝牙硬件设备收到后再发送数据,这样能很好的保证数据不丢失。
3) 最好的办法是1)和 2)的结合,即发送准备好的指令,而后让智能硬件发送数据,而后在接收数据的过程当中,使用ACK机制确保数据没有任何丢失。post

4.. Read/Write Characteristic/Descriptor 等都是异步的,即当即返回,等待回调。所以若是Android手机底层自身若是没有作请求的同步顺序执行的话,那么当有不少请求几乎同时进行时,回调顺序是没法保证的。此时就形成错误,这也会致使不少蓝牙4.0不能兼容某些Android的缘由,所以须要本身提供一套同步机制,如RequestQueue,来保证request&response 一个接一个高效有序的进行,即下一个request必须等到上一个request的response返回以后再执行。线程

5.. Read/Write Characteristic/Descriptor/RemoteRssi(),通常在不一样的线程中回调。(除了onDescriptorWrite返回的线程与写入线程为同一个线程???)xml

BluetoothDevice.conncectGatt(),
BluetoothGatt.connect(),
BluetoothGatt.disconnect(),
BluetoothGatt.discoverServices()
最好都在主线程,不然会遇到不少意想不到的麻烦。ip

6.. BLE的特征一次读写最大长度20字节。get

7.. Android手机会对链接过的BLE设备的Services进行缓存,若设备升级后Services等有改动,则程序会出现通信失败。此时就得刷新缓存,反射调用BluetoothGatt类总的refresh()方法。同步

8.. startLeScan(UUID[], LeScanCallback)在Android 4.4及如下手机中彷佛只支持16位的短UUID,不支持128位。

9.. connectGatt() 在某些三星手机上只能在UI线程调用。

10.. Android L 新API扫描设备换为 startScan(List, ScanSettings, ScanCallback)。

11.. Android M 必须拥有定位权限才能扫描BLE设备。

12.. 一个主设备(例如Android手机)能够同时链接多个从设备(通常为6个,例如智能硬件。超过就链接不上了),一个从设备只能被一个主设备链接,一旦从设备链接上主设备,就中止广播,断开链接则继续广播。在任什么时候刻都只能最多一个设备在尝试创建链接。若是同时对多个蓝牙设备发起创建Gatt链接请求。若是前面的设备链接失败了,则后面的设备请求会被永远阻塞住,不会有任何链接回调。因此建议:若是要对多个设备发起链接请求,最好是一个接一个的顺序同步请求管理。

13.. 任何出错,超时,用完就立刻调用Gatt.disconnect(), Gatt.close()。

14.. 从bindService 到 onServiceConnected 这个回调花费时间较长, onServiceConnected 这个回调极可能在 MainActivity onResume以后才执行, 因此不要期望onResume里去执行扫描,由于此时serviceConnected 回调都还没有执行

15.. getBtAdapter().enable()是异步,当即返回,但从 off 到 on 的过程须要一个时间因此只能监听系统broadcast发出的intent里的state

16.. onCharacteristicWrite … 等等是指本机写数据指令已经成功发送出去,而且智能硬件已经处理完回应回来了,另外,当智能硬件端要求发送的指令有顺序的话,那么这边不能发送速度过快,即不能在onCharacteristicWrite里当即发送下一条指令。例如OAD/OTA等等,字节必须严格按照image的字节顺序发送出去。
17.. 在writeCharacteristic时,若速度过快(例如在OAD时),会发现发送出去的数据有可能不是你本身真正发出去的,在onCharacteristicWrite里打印出能够肯定。
18.. App端的关于同一个UUID的2个指令不能同时发出去,这样会致使硬件端没法辨识,因此须要串行发送,即等其中一个发送回调成功以后,再进行下一个。

19.. 屡次扫描蓝牙,在华为荣耀,魅族M3 NOTE 中有的机型,会发现屡次断开–扫描–断开–扫描… 会扫描不到设备,此时须要在断开链接后,不能当即扫描,而是要先中止扫描后,过2秒再扫描才能扫描到设备。

20.. 扫描尽可能不要放在主线程进行,能够放入子线程里。否则有些机型会出现 do too many work in main thread.