一步一步實現Android低功耗藍芽(BLE)基本開發
專案需要接入兩個低功耗藍芽裝置(BLE),並且與之互動(讀/寫)資料,所以看了下官方對於這塊兒的 ofollow,noindex">介紹 ,總結了一下BLE開發中一些需要注意的地方以及基本流程。
BLE開發需要Android 4.3 (API level 18) 及以上
一.新增許可權
為了能正常使用藍芽相關功能(掃描等),首先需要新增以下許可權:
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
在Android6.0及以上系統中,我們需要動態申請許可權,這裡推薦使用 RxPermissions
簡單介紹下RxPermissions如何引入。
1.在根build檔案中新增程式碼:
... allprojects { repositories { maven { url 'https://jitpack.io' } } } ...
2.在對應moudle的build檔案中新增依賴:
... dependencies { implementation 'com.github.tbruyelle:rxpermissions:0.10.2' } ...
3.使用:
RxPermissions rxPermissions=new RxPermissions(this); rxPermissions.request(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION) .subscribe(granted -> { if (granted) { //許可權允許成功 } });
如果想了解RxPermissions更多用法, 戳這裡 。
二.判斷裝置是否支援藍芽
這裡有兩種處理方式:
- 如果你想讓只有支援BLE的手機才能安裝你的應用程式的話,可以在清單檔案中新增如下內容,這樣的話如果裝置不支援BLE的話你的應用都裝不上,當然這種方式不太友好:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
-
在程式碼中判斷當前裝置是否支援BLE,以對使用者做出反饋。
首先,在清單檔案中宣告需要使用BLE特性,不過required這裡設定為false,然後在app執行時通過
PackageManager.hasSystemFeature()
來判斷裝置是否支援ble:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
// Use this check to determine whether BLE is supported on the device. Then // you can selectively disable BLE-related features. if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); }
三.掃描藍芽裝置
BLE裝置的掃描由BluetoothManager物件提供方法來實現,有兩個掃描方法:
public boolean startLeScan(BluetoothAdapter.LeScanCallback callback) { throw new RuntimeException("Stub!"); } public boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback) { throw new RuntimeException("Stub!"); }
第二個方法允許我們提供特定的UUID,來掃描特定的裝置,掃描結果通過BluetoothAdapter.LeScanCallback介面回撥給我們:
public interface LeScanCallback { /** * Callback reporting an LE device found during a device scan initiated * by the {@link BluetoothAdapter#startLeScan} function. * * @param device Identifies the remote device * @param rssi The RSSI value for the remote device as reported by the *Bluetooth hardware. 0 if no RSSI value is available. * @param scanRecord The content of the advertisement record offered by *the remote device. */ public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord); }
四.獲取遠端BLE裝置
在掃描出裝置以後,我們一般會選擇某個掃描出來的裝置,通過其地址獲取一個遠端的藍芽裝置物件。
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address)
五.連線BLE裝置的GATT服務
與BLE裝置互動的第一步是連線到它,更具體地說,連線到裝置上的GATT服務。要在BLE裝置上連線到GATT服務,可以使用connectGatt()方法。該方法接受三個引數:一個上下文物件、autoConnect(布林值表示是否在BLE裝置可用時自動連線到該裝置),以及對BluetoothGattCallback的引用:
mBluetoothGatt = device.connectGatt(context, true, mGattCallback);
以上程式碼可以連線到由BLE裝置託管的GATT服務,並返回一個BluetoothGatt例項,然後可以使用它來執行GATT客戶端操作,例如寫資料等。呼叫者(Android應用程式)是GATT客戶端。連線狀態,以及GATT的資料變化等通過BluetoothGattCallback介面回撥給客戶端(APP)。
一般使用BluetoothGattCallback的這些回撥方法:
1.獲取連線狀態,在連線成功時掃描裝置服務
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { if (connectChangedListener != null) { connectChangedListener.onConnected(); } mConnectionState = STATE_CONNECTED; mBluetoothGatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { if (connectChangedListener != null) { connectChangedListener.onDisconnected(); } mConnectionState = STATE_DISCONNECTED; } }
2.獲取服務,特性等
一個BLE裝置可能有多個服務 BluetoothGattService
,同樣每個服務可以有多個BluetoothGattCharacteristic特性。
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { List<BluetoothGattService> services = mBluetoothGatt.getServices(); for (int i = 0; i < services.size(); i++) { HashMap<String, BluetoothGattCharacteristic> charMap = new HashMap<>(); BluetoothGattService bluetoothGattService = services.get(i); String serviceUuid = bluetoothGattService.getUuid().toString(); List<BluetoothGattCharacteristic> characteristics = bluetoothGattService.getCharacteristics(); for (int j = 0; j < characteristics.size(); j++) { charMap.put(characteristics.get(j).getUuid().toString(), characteristics.get(j)); } servicesMap.put(serviceUuid, charMap); } BluetoothGattCharacteristic bluetoothGattCharacteristic = getBluetoothGattCharacteristic(UUID_SERVICE, UUID_CHARACTERISTIC); if (bluetoothGattCharacteristic == null) return; enableGattServicesNotification(bluetoothGattCharacteristic); } else { Log.w(TAG, " --------- onServicesDiscovered received: " + status); } }
在上面的程式碼中,我們將BLE裝置的所有 BluetoothGattService
和 BluetoothGattCharacteristic
全部儲存下來,但是在實際需求中,我們一般只會與某個特定 BluetoothGattService
中的某個特性 BluetoothGattCharacteristic
進行資料讀寫。判斷條件就是這裡的 UUID_SERVICE
和 UUID_CHARACTERISTIC
,這兩個UUID一般提供BLE裝置的時候會一併提供給我們。
找到這個特定的BluetoothGattCharacteristic後,我們希望它發生改變時可以得到通知,可以使用setCharacteristicNotification()方法為特性設定通知:
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(UUID_DESCRIPTOR)); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor);
3.監聽資料變化
經過以上設定,我們就可以在 onCharacteristicChanged
回撥方法中獲取BLE裝置發過來的資料了:
@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { //解析資料 parseData(characteristic); }
當然,我們也可以用 第五步
中獲取的mBluetoothGatt來向BLE裝置傳送資料:
mBleGattCharacteristic.setValue(HexUtil.hexStringToBytes(value)); boolean b=mBluetoothGatt.writeCharacteristic(mBleGattCharacteristic);
以上,就是Android端與BLE裝置通訊的基本開發流程,這裡我抽成了一個Demo,專案目錄如下:

幾點說明:
- 因為我這裡需求是接入兩個BLE裝置,所以我抽取了一個BluetoothLeDeviceBase,代表基類裝置,將一些通用的屬性和操作封裝在了這裡
- BluetoothLeDeviceA,BluetoothLeDeviceB代表具體的某個BLE裝置,每個裝置可能有不同之處,例如資料解析方式等。
完整程式碼地址: https://github.com/SolveBugs/BlogPracticeDems ,目前只是基本的封裝,後續會繼續完善。
選擇bluetoothbledemo這個moudle執行即可,介面如下:
