开发过Android 蓝牙4.0 BLE的同学都知道,Android的蓝牙开发有非常多的坑,具体坑的集合可见我的前面一篇博客,《Android蓝牙4.0 BLE开发坑总结》,其中不同机型之间的兼容性就是一个很令人头疼的问题,很多问题究其原因是在手机端和智能设备之间发送请求指令和回调时,其方式是异步请求的,即请求完立即结束,等待回调,而回调又在不同的线程中,因此当交互比较频繁并且之间有依赖关系的时候,很容易错误。很多Android手机针对这种情况在底层实现了对蓝牙请求的同步,即有一个requestQueue,使得一个蓝牙请求能够在上一个蓝牙请求的reponse结束之后再发送,保证了顺序执行和正确性。但是有些Android手机没有提供这样的requestQueue,使得错误率大大增加,往往这些问题还不容易发现,从而造成了Android蓝牙开发的兼容性问题。所以,要想做好兼容性,我们必须在app端的应用层就应该提供一套完整的requestQueue请求同步策略。
那么这篇文章,我就给大家介绍一个解决方案,能够使得activity非常轻量级的只管发送蓝牙请求和接受最终数据即可,其他的就交给library来做。具体的全套代码可见:https://github.com/xpg/GizwitsBLE
其中关键的是BleLibrary里的两个类,AndroidBle 和 BleService,其中AndroidBle是UI 和 BleService 之间的桥梁,基本的从发送蓝牙请求到接收蓝牙数据的过程,我们以BluetoothLeGatt Project里的DeviceControlActivity为例:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_connect:
mBle.requestConnect(mDeviceAddress);
return true;
case R.id.menu_disconnect:
mBle.disconnect(mDeviceAddress);
onDeviceDisconnected();
return true;
case android.R.id.home:
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
其中的mBle.requestConnect(mDeviceAddress);就是利用AndroidBle发送了一条蓝牙请求,那么我们看requestConnect做了什么。
@Override
public boolean requestConnect(String address) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt != null && gatt.getServices().size() == 0) {
return false;
}
mService.addBleRequest(new BleRequest(RequestType.CONNECT_GATT, address));
return true;
}
这里实际上又利用了BleService的强大的requestQueue管理,即mService.addBleRequest(new BleRequest(RequestType.CONNECT_GATT, address));,我们接下来重点看看BleService的requestQueue是如何实现的。
protected void addBleRequest(BleRequest request) {
synchronized (mRequestQueue) {
mRequestQueue.add(request);
processNextRequest();
}
}
这里很简单,就是把request放入requestQueue中,然后处理Queue中的Request。
/**
* 1. 处理 mRequestQueue 里的当前请求,同时开启对 当前请求的处理超时 startTimeoutThread
* 2. 如成功处理完此请求之后,比如 discoverServices,connect,characteristicNotification,readCharacteristic 等,在AndroidBle 里有对应的回调,回调里会调用 bleServiceDiscovered 等
* 3. bleServiceDiscovered 把内容广播出去
* 4. 紧接着调用 requestProcessed, 其作用是
* a. 取消当前请求的超时处理 clearTimeoutThread
* b. 处理下一个请求
*
* 综上,RequestQueue, 只有当 1)request处理完时才会执行下一个request 2)ret失败 / request超时时才会执行下一个
* 否则mCurrentRequest一直指向当前request,
* 将必须等待回调处理完后,调用 BleService.bleGattXXX --> a) broadcast b) reqeustProcessed (即这里的1)) --> 下一个request
*/
private synchronized void processNextRequest() {
if (mCurrentRequest != null) {
return;
}
if (mRequestQueue.isEmpty()) {
return;
}
mCurrentRequest = mRequestQueue.remove();
Log.d(TAG, "+processrequest type " + mCurrentRequest.type + " address "
+ mCurrentRequest.address + " remark " + mCurrentRequest.remark);
startTimeout