藍牙4.0分為標准藍牙和低功耗藍牙(BLE),標准藍牙就是手機上用的那種,低功能耗藍牙由於其具有最大化的待機時間、快速連接和低峰值的發送和接收特性,被廣泛用於智能手表、智能手環等可穿戴設備上。在安卓4.3之前,安卓平台上的BLE開發相當難搞,好在谷歌在4.3之后發布了官方的API。在安卓5.0之后又引入了新的API,原來的API已經被廢棄。在新的系統里采用舊API開發的APP仍可使用,但采用新API開發的APP只能在LOLLIPOP即安卓5.0及其以后的版本使用。
標准藍牙的的開發和BLE不同。標准藍牙連接里有兩個角色一個是客戶端一個是服務器,當客戶端搜索到藍牙服務器后並與之配對后,才能通過UUID(這個是唯一的,服務器端必須與客戶端一致)建立socket,然后使用流像文件讀寫和網絡通信那樣傳輸數據就行了。在BLE里,變成了中心設備(central)和外圍設備(peripheral),中心設備就是你的手機,外圍設備就是智能手環一類的東西。開發BLE的應用都得遵守Generic Attribute Profile (GATT),一個BLE藍牙設備包含多個service,每個service又包含多個characteristic。每個characteristic有一個value和多個descriptor,通過characteristic中心設備與外圍設備進行通信。descriptor顧名思義,包含了BLE設備的一些信息。不同service、characteristic和descriptor都有各自己唯一的UUID。想要跟BLE設備通信,首先通過UUID獲取目標服務,然后再通過UUID獲取characteristic,charateristic起着載體的作用,通過writeCharacteristic()和readCharacteristic(),可以寫入和讀出信息。每個characteristic都有一些自己的屬性,其中在property里,說明了該characteristic的屬性,例如READ|WRITE|WRITE_NO_RESPONSE|NOTIFY。
以下是關鍵代碼:
1.首先,在AndroidManifest.xml里加入
<uses-feature android:name="android.bluetooth.le" android:required="true"/> //Android 5.0之前是android.hardware.bluetooth_le <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.BLUETOOTH"/>
使用新API時,最好在調用方法前判斷下系統版本,否則會出現意想不到的錯誤。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ... }
2.獲取BluetoothAdapter。
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);//這里與標准藍牙略有不同 BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); /*隱式打開藍牙*/ if (!bluetoothAdapter.isEnabled()) { bluetoothAdapter.enable(); }
3.然后獲取BluetoothLeScanner,通過startScan()方法掃描周圍的BLE設備。當搜索到目標設備或者搜索一定時間后通過BluetoothScanner的stopScan()方法停止搜索。
BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();
scanner.startScan(leCallback);
leCallback是一個回調函數,通過onScanResult()把每次搜索到的設備添加到本地。leCallback還有一個方法onBatchScanResults(),按理說應該是批量處理搜索到的結果,但是不知道為什么在我這里無法調用。
leCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { BluetoothDevice device = result.getDevice(); if (!devices.contains(device)) { //判斷是否已經添加 devices.add(device); } deviceAdapter.notifyDataSetChanged(); } } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); Log.e("搜索失敗"); } };
4.開始連接。
BluetoothGatt bluetoothGatt = device.connectGatt(MainActivity.this, false, new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { gatt.discoverServices(); //連接成功,開始搜索服務,一定要調用此方法,否則獲取不到服務 } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { Log.e(TAG,gatt.getDevice().getName() + " write successfully"); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { String value = UtilOnStr.parseBytesToHexString(characteristic.getValue()); Log.e(TAG,gatt.getDevice().getName() + " recieved " + value); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { String response = UtilOnStr.parseBytesToHexString(characteristic.getValue()); Log.e(TAG, "The response is "+ response); } });
5.寫入和讀取數據。
BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID); if(service != null) { characteristic = service.getCharacteristic(CHARACTERISTIC_UUID); if (characteristic !=null) { bluetoothGatt.setCharacteristicNotification(characteristic, true); //設置characteristic的通知,觸發bluetoothGatt.onCharacteristicWrite()事件。 characteristic.setValue(UtilOnStr.parseHexStringToBytes(value)); bluetoothGatt.writeCharacteristic(characteristic); } }
當連接上BLE設備后,調用discoveryServices()發現服務,通過SERVICE_UUID獲取目標service,如果service不為空,再通過CHARACTERISTIC_UUID獲取characteristic,借助characteristic寫入指定值與BLE設備進行通信。這里要注意的是characteristic接收的是一個byte數組,而且讀寫的方法都是異步的。調用bluetoothGatt.readCharacteristic(characteristic)可讀取BLE設備返回的值。bluetoothGatt的writeCharacteristic()方法會觸發BluetoothGattCallback里的onCharacteristicWrite(),相應的,bluetoothGatt的readCharacteristic()方法會觸發onCharacteristicRead()。
對中心設備與外圍設備的傳輸數據的處理發生在onCharacteristicChanged()里,當characteristic寫入了正確的數值后,會激活BLE設備,不時地返回數據。
當不知道BLE設備的service和characteristic的對應的UUID時,可以在回調函數里的onServicesDiscovered()方法里,通過BluetoothGatt的getServices()獲得該設備所有的service,然后再調用service的getUuid()得到其UUID,同理通過service的getCharacteristics()可以得到每個service所有的characteristic和其UUID。然后一個個不厭其煩的嘗試,找到正確的service和characteristic(我就是這樣干的)。
6.總結
由於網上關於Android 5.x 低功耗藍牙 BLE開發的資料不是很多,我才下決心寫這篇文章的,希望能對做相關開發犯過迷糊的人有一點兒幫助。同時我也是個安卓開發的新手,對藍牙串口通信也是一知半解,表述難免有紕漏,理解也不到位,如果有幸被大家看到這篇文章我會倍感欣慰,並希望能指出不足之處。