• 对蓝牙设备的操作不能并行,只能串行,并且每次都要在收到上一个操作的回调后才能继续下一个操作。有时候蓝牙协议栈出现异常可能收不到回调,所以我们要对每个操作做超时检查,否则后面的所有操作都被阻塞了。对于这些超时的任务或者失败的任务,通常是连接出了问题,我的做法是直接给gatt关掉,下次重新连接的时候重开一个gatt。除超时处理之外,对每个任务最好支持失败重试机制,尤其是连接。
  • 同一个设备的所有操作最好都放在同一个线程串行执行,最好不要放在UI线程,虽然这些操作都是异步的,理论上来说不会耗时,但是由于涉及到跨进程,有可能出现ANR。另外不建议每个设备都开一个线程,设备多了会费内存也会降低性能。较好的做法是开一个线程,所有设备的操作都在该线程中发起,虽然占用同一个线程,但是每个设备各自维护自己的任务队列。
  • 设备的gatt在不用时要及时关闭,不然会造成连接泄露,而系统支持的连接设备数是有限的,当达到上限后有可能其它设备连不了。
  • 当设备断开连接时,最好closeGatt,而不是diconnect。不要下次复用之前的gatt来reconnect,因为有的手机上重连可能会存在问题,比如重连后死活发现不了service。这种情况下,最好只要断开连接就close gatt,下次连接时打开全新的gatt,这样就可以发现service了。
  • service不要缓存,虽然uuid什么的都没变,但是这些service都会和gatt关联的,如果gatt变了,那service就报废了,对这些service和character做任何读写操作都会出错。所以建议每次连接上时都去discover service,不要缓存。
  • 有的手机discover service很慢,原因是connect interval太大了,有的手机会主动向设备发起更改connect interval,而有的手机却不会。这样的话connect interval相差就会很大,实践中发现有的手机是7ms,有的手机是默认的50ms,所以发现service都要8s,甚至20s的都很寻常,这对用户来说是无法忍受的。所以比较好的办法是设备主动发起更改connect interval,这个可以参考google搜索 ble spec update connection parameter, https://devzone.nordicsemi.com/question/12545/update-connection-parameter-programmatically/
  • 前面提到蓝牙的所有操作都要放到同一个线程里执行,但是回调通常都会在binder线程里执行,因为这是跨进程回调回来的。一定要注意到这一点,否则会出现一些奇怪的问题。比如writeCharacter在线程A中,但是onCharacterWrite是在binder线程中,回调里如果涉及到任务队列的调度一定要post回线程A中,不然任务调度会出问题。
  • 当设备固件升级后,character可能发生了变化,而系统是不知道的,下次discover service的时候还是返回的旧的缓存,这样读写character可能会失败。解决办法是固件升级后,断开连接再重开一个gatt,并马上刷新一下该设备的缓存。当然,重启蓝牙也会刷新缓存,不过会影响到所有设备。另外有时候discoverService服务发现的不全,或者根本发现不了服务,也可以考虑清除一下缓存。 关于蓝牙缓存的清除可以参考 http://stackoverflow.com/questions/22596951/how-to-programmatically-force-bluetooth-low-energy-service-discovery-on-android
  • 尽量维护设备的连接,因为连接是有成本的,慢的时候可能要等几秒钟,对于操作频次较高的设备用户是无法忍受的。这种情况可以考虑在退出设备操作页面时,继续为该设备保持连接一段时间再断开,如果这段时间内用户又重新点进设备操作页就能很快。
  • 固件升级通常是写设备,为加快写速度,可以在write character时指定no response标志,实践发现速度可以提升2~3倍。不过要注意的是即便带了no response标志,也不代表这种写操作是没有回调的,我们仍然要遵循收到上一次写回调后才能进行下一次写操作。
  • 打开/关闭character的notify,如下:
private boolean setCharacteristicNotification(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, boolean flag) {
    boolean result = gatt.setCharacteristicNotification(characteristic, flag);

    if (result) {
        BluetoothGattDescriptor descriptor = characteristic
         .getDescriptor(UUID.fromString(BluetoothConstants.CLIENT_CHARACTERISTIC_CONFIG));

        if (descriptor != null) {
            descriptor.setValue(flag ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
                            : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        }
        result = mBluetoothGatt.writeDescriptor(descriptor);
    }
    return result;
}

这里有两步操作,别漏掉设置descriptor,并且要注意必须等收到onDescriptorWrite回调之后再继续执行下一个任务。

最后可以参考这个链接,关于BLE各种不稳定的问题和兼容的办法的讨论:
http://stackoverflow.com/questions/17870189/android-4-3-bluetooth-low-energy-unstable

大家可以参考这个蓝牙开源框架BluetoothKit:
https://github.com/dingjikerbo/BluetoothKit