Android蓝牙低功耗(BLE)模块设计
2016-07-31 11:20
267 查看
在阅读这篇文章之前你应该对
连接设备
发现服务
断开连接
关闭GATT
实际开发过程中我将连接到发现服务合并在一起了,因为如果连接成功但是发现服务没有成功时GATT也是不可用的。还有断开连接这个功能我在使用过程中也用的非常少一般都是关闭GATT释放资源。同时定义一些通用的错误信息如:蓝牙不可用、没有扫描到设备等错误信息。
定义接口后就是如何来实现以上接口的问题。在实现过程中是利用Android Handler机制来实现的,每一个
在BaseBluetoothGattCallback中会回调
GATT和Android蓝牙框架有一定的了解。这里不会向你解释
Service、
Characteristics等蓝牙知识。这里只是我写下我对Android Ble的再次封装来适应APP的业务需求。
BLE模块
在开发时APP需要连接多个Ble设备,可能很多人会想Ble这种长时间运行的程序应该写进Android Service里面。对的写入Service是必须的,但是写入的方法也是对APP有很大的影响的。如果你把所有的Ble连接、数据交互都写入Service中一但Service被杀APP的BLE模块就失效你想再次去连接只能自己开启Service或等到Android调试Service。我的实现方法BLE模块不依赖Service仅仅只是在Service中运行即使Service被杀BLE模块还在对APP不会有任何影响。抽象GATT
定义IGattClient接口来抽象出BLE的连接的管理。GATT的行为大致可分为:
连接设备
发现服务
断开连接
关闭GATT
实际开发过程中我将连接到发现服务合并在一起了,因为如果连接成功但是发现服务没有成功时GATT也是不可用的。还有断开连接这个功能我在使用过程中也用的非常少一般都是关闭GATT释放资源。同时定义一些通用的错误信息如:蓝牙不可用、没有扫描到设备等错误信息。
package im.xingzhe.ble.base; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; public interface IGattClient { int ERROR_SHIFT = 8; //100000000 int STATE_SHIFT = 0; // int STATE_IDLE = -1; int STATE_CONNECTING = BluetoothGatt.STATE_CONNECTING ; int STATE_CONNECTED = BluetoothGatt.STATE_CONNECTED ; int STATE_DISCONNECTING = BluetoothGatt.STATE_DISCONNECTING ; int STATE_DISCONNECTED = BluetoothGatt.STATE_DISCONNECTED ; int STATE_SERVICES_DISCOVERED = 0x8; int ERROR_NONE = 0; int ERROR_BLUETOOTH = 0x1 << ERROR_SHIFT ; int ERROR_TIMEOUT = 0x2 << ERROR_SHIFT; int ERROR_CONNECT_LOSE = 0x8 << ERROR_SHIFT; int ERROR_NOT_FOUND_DEVICE = 0x10 << ERROR_SHIFT; //4096 int ERROR_DEVICE_BUSY = 0x11 << ERROR_SHIFT; int ERROR_UNKNOWN = 0x12 << ERROR_SHIFT; BluetoothDevice getBluetoothDevice(); String getName(); String getAddress(); BluetoothGatt getBluetoothGatt(); int getLastError(); void connect(); void discoverServices(); void disconnect(); void close(); int getConnectionState(); void registerConnectionListener(ConnectionListener listener); void unregisterConnectionListener(ConnectionListener listener); interface ConnectionListener { void onStateChanged(IGattClient client, int newState); } }
定义接口后就是如何来实现以上接口的问题。在实现过程中是利用Android Handler机制来实现的,每一个
GattClient中都有一个Handler来处理连接、发现服务、断开连接和关闭Gatt。由于篇幅原因
IGattClient实现代码我就不全部贴出来了只拿出部分来讲解一下。
定义通用Handler
通过Handler机制来同步处理Gatt基本操作这样维护起来也比较方便同样可以保持设备的状态不会乱掉。
package im.xingzhe.ble.base; import android.os.Handler; import android.os.Looper; import android.os.Message; public class GattClientHandler extends Handler { AbsGattClient mClientRef; public GattClientHandler(AbsGattClient client, Looper looper) { super(looper); this.mClientRef = client; } @Override public void handleMessage(Message msg) { AbsGattClient client = mClientRef; try{ if( client != null) client .handleMessage(msg); } catch (Exception exception){ client.e(exception); } } }
同步状态与发现服务
由于IGattClient要维护自己的设备,有时候这些状态是由程序主动发起的也有可能由于设备信号不足导致的。不管怎样这些状态Android BLE框架中都会有回调。Android通过
BluetoothGattCallback来回调
Gatt状态,其中
onConnectionStateChange这个方法是用来告诉我们蓝牙设备连接已经改变。我们应该这个方法中刷新
IGattClient中维护的状态。这里我定义了
BaseBluetoothGattCallback来同步状态。
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public class BaseBluetoothGattCallback<CLIENT extends AbsBleDevice> extends BluetoothGattCallback { public CLIENT mClientRef; public BaseBluetoothGattCallback(CLIENT client) { this.mClientRef = client; } @Override public final void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if(mClientRef != null){ try{ mClientRef.syncState(status, newState); }catch (Exception e){ mClientRef.e(e); } } } @Override public final void onServicesDiscovered(BluetoothGatt gatt, int status) { if(mClientRef != null){ try{ mClientRef.handleServicesDiscovered(status); }catch (Exception e){ mClientRef.e(e); } } }
在BaseBluetoothGattCallback中会回调
AbsGattClient的
syncState与
handleServicesDiscovered方法。如果你要问我既然Android中已经维护了状态为什么我们的实现中还要自己去维护。我只能说因国内的Android机型太多系统大都是深度定制。比如说:你把设备电池下了或使信号丢失,正常情况下
status是
BluetoothGatt.GATT_SUCCESS,
newState会是
BluetoothProfile.STATE_DISCONNECTED然而有些手机并不会这样给你回调。
public void syncState(int status, int newState) { d(String.format("onConnectionStateChange: status->%d, newState->%d", status, newState)); /* 不应该依赖系统API去判断一个连接是成功还是失败,使用AbsGattClient内部维护的状态码来 决定操作 */ if (status == BluetoothGatt.GATT_SUCCESS && newState == mTargetState) { //是期望的状态 refreshGattClientState(mTargetState, mTargetState); } else { int currentState = mCurrentState; //保存当前状态 int targetState = mTargetState; mError = ERROR_UNKNOWN; if (currentState == STATE_CONNECTING) { mError = ERROR_DEVICE_BUSY; } else if (newState == STATE_DISCONNECTED) { closeBluetoothGatt(); refreshGattClientState(STATE_DISCONNECTED, STATE_DISCONNECTED); if ((targetState == currentState) && (/*currentState == STATE_CONNECTED || */currentState == STATE_SERVICES_DISCOVERED)) { /* 本地记录是已连接状态但可能由于信号或设备休眠导致失去连接 */ mError = ERROR_CONNECT_LOSE; printError(); if (isAutoConnection()) { reconnect(); } } } } wakeup(); }
连接处理
我将设备的连接和其他行为设计成等待的机制如:设备连接时会首先调用mBluetoothDevice.connectGatt(mAppContext, false, mGattCallback);然后阻塞
initLocalScheduler();中启动的线程同时等待
syncState方法的的唤醒。当前也要有超时机制不然整个设备没法用了。
protected synchronized void initLocalScheduler() { if (mLocalHandler != null) { return; } HandlerThread ht = new HandlerThread(getName() == null ? getAddress() : getName()); ht.setUncaughtExceptionHandler(this); ht.start(); mLocalHandler = new GattClientHandler(this, ht.getLooper()); } protected void _connect() { d("try to connect to " + getName()); mError = ERROR_NONE; mNotify = false; refreshGattClientState(STATE_CONNECTING, STATE_CONNECTED); //检测蓝牙是否可用 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { refreshGattClientState(STATE_DISCONNECTED, STATE_DISCONNECTED); mError = ERROR_BLUETOOTH; return; } if (mBluetoothDevice == null) { if (mDeviceAddress != null) { mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(mDeviceAddress); } if (mBluetoothDevice == null) { mError = ERROR_NOT_FOUND_DEVICE; refreshGattClientState(STATE_DISCONNECTED, STATE_DISCONNECTED); return; } } mConnectingDevices.incrementAndGet(); mBluetoothGatt = mBluetoothDevice.connectGatt(mAppContext, false, mGattCallback); if (mBluetoothGatt == null) { mError = ERROR_BLUETOOTH; refreshGattClientState(STATE_DISCONNECTED, STATE_DISCONNECTED); } else { waitForRemoteDevice(DEFAULT_CONNECT_TIMEOUT * mConnectingDevices.get()); if (mCurrentState == STATE_CONNECTED) { _discoverServices(); if (mCurrentState == STATE_SERVICES_DISCOVERED) { //连接成功后清除下Message mLocalHandler.removeMessages(OP_CONNECT); mLocalHandler.removeMessages(OP_RECONNECT); mConnectingDevices.decrementAndGet(); return; } } closeBluetoothGatt(); } mConnectingDevices.decrementAndGet(); refreshGattClientState(STATE_DISCONNECTED, STATE_DISCONNECTED); } private void waitForRemoteDevice(int millis) { d("waitForRemoteDevice: " + (millis)); try { long start = SystemClock.uptimeMillis(); synchronized (mLock) { //如果返回太快,将会导致唤醒失败 if (!mNotify) mLock.wait(millis); if (!mNotify) { mError = ERROR_TIMEOUT; } else { //mCurrentState会在别处更新 } } d("waitForRemoteDevice return: " + (SystemClock.uptimeMillis() - start)); } catch (InterruptedException e) { e.printStackTrace(); refreshGattClientState(STATE_DISCONNECTED, STATE_DISCONNECTED); } }
抽象BEL设备
IGattClient抽象完成后接下来就是定义BLE设备。我只贴出
AbsBleDevcie代码其中只是实现了一些蓝牙标准中的
Service的处理。
public abstract class AbsBleDevice extends AbsGattClient implements IBleDevice { public static UUID CLIENT_CHARACTERISTIC_CONFIG2 = UUID.fromString(BLEAttributes.CLIENT_CHARACTERISTIC_CONFIG2); public static UUID BLE_BATTERY_SERVICE = UUID.fromString(BLEAttributes.BLE_BATTERY_SERVICE); public static UUID BLE_BATTERY_CHARACTERISTIC = UUID.fromString(BLEAttributes.BLE_BATTERY_CHARACTERISTIC); public static UUID BLE_DEVICE_INFORMATION_SERVICE = UUID.fromString(BLEAttributes.BLE_DEVICE_INFORMATION_SERVICE); public static UUID BLE_DEVICE_INFORMATION_FIRMWARE_CHARACTERISTIC = UUID.fromString(BLEAttributes.BLE_DEVICE_INFORMATION_FIRMWARE_CHARACTERISTIC); private static final int OP_READ_BATTERY = 0x1; private static final int OP_READ_FIRMWARE = 0x2; private static final int OP_SET_NOTIFICATION = 0x3; private Device mLocalDevice; public AbsBleDevice(Device localDevice){ this.mLocalDevice = localDevice; } @CallSuper @Override protected void onStateChanged(int currentState, int targetState) { if(currentState == STATE_SERVICES_DISCOVERED) { onServicesDiscovered(); }else if(currentState == STATE_DISCONNECTED){ onDeviceDisconnected(); } } protected void onServicesDiscovered(){ } protected void onDeviceDisconnected(){ } public Device getLocalDevice(){ return this.mLocalDevice; } public int getDeviceType(){ return mLocalDevice != null ? mLocalDevice.getType(): Device.TYPE_UNKNOW; } public boolean isServicesDiscovered( ){ return getConnectionState() == STATE_SERVICES_DISCOVERED; } public void readFirmwareVersion(){ if(isServicesDiscovered()){ Message message = mLocalHandler.obtainMessage(OP_READ_FIRMWARE); mLocalHandler.sendMessageDelayed(message, 500); } } private void _readFirmwareVersion(){ BluetoothGattService deviceInfoService = mBluetoothGatt.getService(BLE_DEVICE_INFORMATION_SERVICE); if(deviceInfoService != null){ BluetoothGattCharacteristic firmwarmCh = deviceInfoService.getCharacteristic(BLE_DEVICE_INFORMATION_FIRMWARE_CHARACTERISTIC); mBluetoothGatt.readCharacteristic(firmwarmCh); } } private void _readBattery(){ BluetoothGattService batteryService = mBluetoothGatt.getService(BLE_BATTERY_SERVICE); if(batteryService != null) { BluetoothGattCharacteristic batteryCharacteristic = batteryService.getCharacteristic(BLE_BATTERY_CHARACTERISTIC); mBluetoothGatt.readCharacteristic(batteryCharacteristic); } } public void readBattery(){ if(isServicesDiscovered()){ Message msg = mLocalHandler.obtainMessage(OP_READ_BATTERY); mLocalHandler.sendMessageDelayed(msg, 300); } } protected void _setCharacteristicNotification2(BluetoothGattCharacteristic characteristic, boolean enabled) { if(characteristic == null) return; BluetoothGatt gatt = getBluetoothGatt(); gatt.setCharacteristicNotification(characteristic, enabled); final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG2); if (descriptor != null) { descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } } public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { if(isServicesDiscovered()){ Message message = mLocalHandler.obtainMessage(OP_SET_NOTIFICATION, enabled ? 1: 0, 0, characteristic); mLocalHandler.sendMessage(message); } } @Override protected void handleMessage(Message message) { super.handleMessage(message); switch (message.what){ case OP_READ_BATTERY: _readBattery(); break; case OP_READ_FIRMWARE: _readFirmwareVersion(); break; case OP_SET_NOTIFICATION: _setCharacteristicNotification2((BluetoothGattCharacteristic) message.obj, message.arg1 != 0); break; } } }
写在最后
其实整个实现过程是比较波折的,但是经过慢慢的摸索现在的我手上这个APP的BLE模块还是比较稳定的。市面上的大部分机型都可以正常工作除了一些相对来说比较老的设备有一些问题。相关文章推荐
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- android 低功耗BLE蓝牙连接示例代码
- Android-低功耗蓝牙BLE(Bluetooth Low Energy)开发
- Android 低功耗蓝牙(BLE)开发(3)-- BluetoothDevice详解
- android 蓝牙低功耗(BLE)非常棒的工具类,获取小米手环的步数
- Android BLE与终端通信(五)——Google API BLE4.0低功耗蓝牙文档解读之案例初探
- Android 低功耗蓝牙(BLE)开发(1)-- 基本概念
- Android低功耗蓝牙(BLE)开发的一点感受
- Android BLE低功耗蓝牙开发
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- Android蓝牙BLE低功耗相关简单总结
- 【Android应用开发】Android 蓝牙低功耗 (BLE) ( 第一篇 . 概述 . 蓝牙低功耗文档 翻译)
- Android 低功耗蓝牙(BLE)开发(2)-- BluetoothAdapter详解
- Android BLE 蓝牙低功耗教程,中央和周边的实现
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- Android BLE学习(二): Android与51822蓝牙模块通信流程的实现与分析
- 使用BleLib的轻松搞定Android低功耗蓝牙Ble 4.0开发详解
- Android 低功耗蓝牙(BLE)开发(4)-- 蓝牙扫描和连接
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现