使用RxJava帮助低功耗蓝牙(BLE)进行通信
Android中的藍牙開發有兩種,一種是傳統藍牙,另一種是低功耗藍牙,這兩者完全不一樣,開發前你得弄清你需要開發的是哪一種,用傳統藍牙的方式進行低功耗藍牙的開發你可能都沒法使你的設備連上藍牙,不要問我為什么知道,說多了都是淚 (TT)
低功耗藍牙(Bluetooth Low Energy)簡稱BLE,常見于各種運動手環、電子血壓計等健康管理設備,Android4.3(API級別18)中引入了面向低功耗藍牙的API支持。也就是說開發的前提是手機設備支持BLE并且系統是Android4.3以上,與手機通信的藍牙設備是低功耗藍牙。
如果你看了官方文檔上的示例,你會發現使用了Handler、和廣播進行異步通信,之前我在公司項目中藍牙功能也是這么寫的,現在有了RxJava,我們可以寫的更優(zhaung)雅(bi)些,所以就有了這篇文章。
本文的代碼地址:RxBleDemo
關于RxJava,如果你還不了解,可以看給 Android 開發者的 RxJava 詳解
關于低功耗藍牙的開發你可以看官方文檔或者直接閱讀本文
整體思路
假定你已經有了一部支持BLE的手機和一個可以通信的低功耗藍牙模塊,那么就可以按下面的步驟開搞了:
- 藍牙權限
- 設置并開啟手機藍牙
- 查找藍牙設備
- 連接藍牙服務
- 進行藍牙通信
詳細步驟
設置藍牙權限
在應用中的manifest文件中聲明藍牙權限
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>復制代碼另外我們可以通過PackageManager.hasSystemFeature()方法來判斷當前的手機設備是否支持BLE。
設置并開啟手機藍牙設備
首先通過BluetoothManager獲取手機中唯一的的藍牙適配器(藍牙發送接收器)BluetoothAdapter對象。再通過BluetoothAdapter開啟手機藍牙設備,代碼如下:
public void initBle(Context context) {this.mContext = context.getApplicationContext();BluetoothManager bluetoothManager =(BluetoothManager) this.mContext.getSystemService(Context.BLUETOOTH_SERVICE);mBleAdapter = bluetoothManager.getAdapter();if (mBleAdapter != null) {mBleAdapter.enable();//不彈對話框直接開啟藍牙}}復制代碼值得一提的是我將藍牙功能簡單的封裝成了一個工具類,用到了靜態內部類的單例模式,為了防止內存泄露,在初始化藍牙的時候傳入了Application的Context對象的引用
查找藍牙設備
定義了一個scanBleDevices(boolean enable)方法用于開啟和關閉掃描。這里使用了BluetoothAdapter.startLeScan(LeScanCallback)方法開啟掃描,需要傳入一個掃描回調:
private BluetoothAdapter.LeScanCallback mBleScanCallback = new BluetoothAdapter.LeScanCallback() {public void onLeScan(BluetoothDevice bleDevice, int rssi, byte[] scanRecord) {if (mIsScanning) {Log.d(TAG, "onLeScan:找到設備" + bleDevice.getName());if (mTargetDeviceName.equals(bleDevice.getName())) {connectDevice(bleDevice);//連接藍牙設備}} else {Log.d(TAG, "onLeScan: 停止掃描");}} };復制代碼 這個回調也用于關閉藍牙掃描的方法BluetoothAdapter.stopLeScan(LeScanCallback),所以定義了一個布爾型變量mIsScanning判斷藍牙掃描的開啟和關閉。
說了這么多,我們的RxJava好像還沒登場。在掃描過程中,我們需要限定藍牙的掃描的超時時間,不能讓手機這么一直掃描,所以我們可以通過RxJava中的timer延時一段時間后執行停止掃描:
連接藍牙服務并接收數據
當查找到名為mTargetDeviceName的目標藍牙設備,就可以通過下面的方法去連接,準確的說是連接設備上的GATT服務:
private void connectDevice(BluetoothDevice bleDevice) {scanBleDevices(false);mBleGatt = bleDevice.connectGatt(mContext, true, new BleGattCallback());mBleGatt.connect();Log.d(TAG, "開始連接設備:" + mBleGatt.getDevice().getName()); }復制代碼連接藍牙設備前需要關閉藍牙掃描。bleDevice.connectGatt(mContext, true, new BleGattCallback())方法返回了一個藍牙GATT對象,這個方法中的true代表自動連接(藍牙模塊斷電重啟后,可以重新連接它),調用GATT的connect()方法進行連接,連接過程中會執行傳入的回調BleGattCallback,這個回調繼承了BluetoothGattCallback并重寫了以下三個方法:
一、onConnectionStateChange
public void onConnectionStateChange(BluetoothGatt bleGatt, int status, int newState) {super.onConnectionStateChange(bleGatt, status, newState);Log.d(TAG, "onConnectionStateChange: 連接狀態: " + newState);if (newState == BluetoothGatt.STATE_CONNECTED) {//連接成功Log.d(TAG, "onConnectionStateChange: 設備連接");bleGatt.discoverServices();//搜索服務} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {//斷開連接Log.d(TAG, "onConnectionStateChange: 設備斷開");} }復制代碼這個方法監聽連接狀態的改變,連接狀態有四個值:
| STATE_CONNECTED | 已連接 |
| STATE_CONNECTING | 正在連接 |
| STATE_DISCONNECTED | 斷開連接 |
| STATE_DISCONNECTING | 正在斷開連接 |
當設備已連接時,需要通過discoverServices()查找GATT服務,查找服務過程中會執行重寫的第二個方法onServicesDiscovered。
二、onServicesDiscovered
可以在此方法中獲取GATT的服務列表,這個服務列表中的每一個服務對應著一個BluetoothGattCharacteristic(用于通信)列表,需要對這個列表通過UUID過濾出我們想要的BluetoothGattCharacteristic,然后就可以拿這個BluetoothGattCharacteristic進行通信了。
關于 UUID
通用唯一標識符 (UUID) 是用于唯一標識信息的字符串 ID 的 128 位標準化格式。 UUID 的特點是其足夠龐大,因此你可以選擇任意隨機值而不會發生沖突。 在此示例中,它被用于唯一標識應用的藍牙服務。 要獲取 UUID 以用于你的應用,你可以使用網絡上的眾多隨機 UUID 生成器之一,然后使用 fromString(String) 初始化一個 UUID。不必過多糾結于UUID。
上面這個過程如果用傳統的方式編寫的話,那就是列表遍歷嵌套列表遍歷再嵌套if判斷,下次再看的話就是一堆迷之縮進,還好可以用RxJava寫出鏈式的結構:
public void onServicesDiscovered(final BluetoothGatt bleGatt, int status) {Log.d(TAG, "onServicesDiscovered: 查找服務: " + bleGatt.getServices().size());List<BluetoothGattService> serviceList = bleGatt.getServices();Observable.from(serviceList).flatMap(new Func1<BluetoothGattService, Observable<BluetoothGattCharacteristic>>() {public Observable<BluetoothGattCharacteristic> call(BluetoothGattService bleGattService) {return Observable.from(bleGattService.getCharacteristics());}}).filter(new Func1<BluetoothGattCharacteristic, Boolean>() {public Boolean call(BluetoothGattCharacteristic bleGattChar) {return bleGattChar.getUuid().toString().equals(UUID);}}).subscribe(new Action1<BluetoothGattCharacteristic>() {public void call(BluetoothGattCharacteristic bleGattChar) {bleGatt.setCharacteristicNotification(bleGattChar, true);//設置開啟接收藍牙數據mBleGattChar = bleGattChar;}}); }復制代碼三、onCharacteristicChanged
此方法用于接收藍牙模塊發送過來的數據,它是異步的,可以用RxJava方便的切換到Android主線程:
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {Log.d(TAG, "onCharacteristicChanged");String receiveData = new String(characteristic.getValue());Log.d(TAG, "收到藍牙發來數據:" + receiveData);Observable.just(receiveData).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<String>() {public void call(String receiveData) {//處理receiveData}}); }復制代碼前面提到了整個藍牙功能是一個工具類,那么我們怎么在我們想要的地方(Activity)接收到這個receiveData呢?也許你會說可以在這里寫一個接口回調啊,是的沒問題。在了解到可以通過RxJava實現EventBus事件總線后,我想可以寫一個簡單的RxBus在這里發射數據,在需要的地方訂閱并接受數據。
關于RxBus,你可以先看implementing-an-event-bus-with-rxjava-rxbus,然后可以看state-propagation-in-android-with-rxjava-subjects(需要科學上網)。此外,國內也有很多相關文章。
先定義一個Subject,它既是觀察者又是被觀察者
private Subject<String, String> mBus = new SerializedSubject<>(PublishSubject.<String>create());復制代碼然后在處理receiveData的地方發射數據
mBus.onNext(receiveData);復制代碼再定義一個方法用于接收數據
public Observable<String> receiveData() {return mBus; }復制代碼最后在需要接收數據的地方訂閱
mRxBle.receiveData().subscribe(new Action1<String>() {public void call(String receiveData) {sendTv.setText(mStringBuffer.append(receiveData).append("\n"));} });復制代碼向藍牙設備發送數據
通信不僅僅是接收數據,還需要發送數據,這個實現起來很簡單,只要使用我們之前拿到的BluetoothGattCharacteristic對象以及BluetoothGatt對象進行相關方法的調用就行,在項目中由于需要對數據進行延時發送,所以也用到了timer:
Observable.timer(time, TimeUnit.MILLISECONDS).subscribe(new Action1<Long>() {public void call(Long l) {if (mBleGatt != null && mBleGattChar != null) {mBleGattChar.setValue(data);//設置數據boolean isSend = mBleGatt.writeCharacteristic(mBleGattChar);//寫入(發送)數據Log.d(TAG, "發送:" + (isSend ? "成功" : "失敗"));}}});復制代碼RxJava的強大之處在于他有各種各樣的操作符,可以對發布的數據源進行各種各樣的處理,實際項目中有很多應用的場景。
最后
我們寫一個簡單的demo進行測試一下,效果如下:
Android端發送Hello,Ble給藍牙模塊,接受藍牙模塊發過來的Hello,Android!
使用調試助手調試藍牙模塊,接受Android端發送過來的Hello,Ble,向Android端發送Hello,Android!
本文的所有代碼地址:RxBleDemo
本文同步于我的個人博客
總結
以上是生活随笔為你收集整理的使用RxJava帮助低功耗蓝牙(BLE)进行通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国内首条不限速高速将于年底开通 初定速度
- 下一篇: (String)、toString、St