三步走--低功耗藍芽BLE開發實戰
BLE是Android4.3以上加入的新功能,他可以很大程度上節省了裝置的功耗,他會在啟用的時候進入一個快速的廣播段,這時候周圍的裝置可以搜尋到BLE裝置,當匹配成功的時候就會建立一個長連線,如果沒有匹配成功,他就會在一段時間後自動進入相對慢速的廣播段,給周圍裝置傳送的廣播頻率也會大大減少,直到沒有裝置與他匹配成功的時候,會自動停止傳送廣播,處於關閉狀態,周圍的裝置也無法搜尋到此BLE裝置。
1.開始掃描
BluetoothAdapter.getDefaultAdapter().startLeScan(null, leScanCallback);//開始掃描,就可以一直搜尋周圍的藍芽2.掃描的結果在callback裡,new一個callback,回撥裡的資訊都在device裡,比如name,mac等等,一般我在這裡會去過濾一些mac地址來指定連線哪個BLE裝置
//3.如果上一步中的device經過一系列判斷是我們自己的device,那麼就執行下一步連線的操作掃描的回撥 BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {if (isConnecting || isConnected() || TextUtils.isEmpty(device.getName()) || TextUtils.isEmpty(device.getAddress()) ) { return;}if (!nameList.contains(device.getName())) {nameList.add(device.getName()); UIUtils.sendBleMes("DEVICENAME", device.getName()); } if(!TextUtils.isEmpty(last_DeviceName)&&last_DeviceName.equals(device.getName())){//如果掃描到上次儲存到sp的裝置,就直接連線 tryConnect(device); return; } if(tryconnect){ if(device.getName().equals(connectingName)){ connectingName = ""; tryconnect = false; tryConnect(device); } } } };
//嘗試連線 private void tryConnect(BluetoothDevice device) { isConnecting = true;//正在嘗試連線 device.connectGatt(context, false, bluetoothGattCallback); }跟上一步一樣,連線這個BLE裝置的結果依然在一個callback中,也是最重要的一個callback,如下:
1.onConnectionStateChange,名字就是意思,即連線狀態的回撥,可以通過與BluetoothProfile下的狀態值進行判斷來知曉裝置的連線狀態,記住,不要以為連線了裝置就完了,在BLE連線到裝置的時候我們並不認為是真正意義上的連線,BLE裡面有很多個service,只有service判斷是我們自己定義的時候才能真正確定為是我們自己的裝置了
所以下面我在判斷連線裝置狀態ok的時候,還會discovewServices(),這個方法會回撥在同樣的這個callback的OnServiceDiscovered方法中
2.OnServiceDiscovered,即判斷service的回撥,如果在這裡判斷服務狀態OK,並且service的uuid跟我們自己BLE裝置service定義的uuid相同,此時恭喜你,你已經找到了自己的裝置了,找到自己的裝置後你還有兩步非常重要的事,1)停止掃描,因為BLE即使成功連線了裝置依然會不停的掃描周圍的裝置,2)我們要開啟這個service下的Characteristic,為什麼要開啟?你可以把他想象成是一個開關,他打開了,你的BLE裝置發出的指令(實際上就是一些Characteristic中包括的value和Descriptor)才能傳送到連線的裝置上
4.onCharacteristicChanged,當上一步你打開了開關後,以後你的BLE裝置傳送的指令都會在這個方法中回撥給你,你可一定一個map集合,裝載你的鍵值對,更好管理
程式碼如下:
//連線的回撥 BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); LogUtils.i("BluetoothGattCallback" + "--onConnectionStateChange()--" + gatt.getDevice().getName() + "," + newState); if (newState == BluetoothProfile.STATE_CONNECTED) { LogUtils.i("onConnectionStateChange:已連線到裝置" + gatt.getDevice().getName() + ",正在搜尋服務"); isConnecting = false;//從未連線的集合中去掉 gatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { LogUtils.i("onConnectionStateChange:裝置斷開連線:" + gatt.getDevice().getName()); isConnecting = false; isConnected = false; ShowToast.show(UIUtils.getContext(), "裝置斷開連線:" + gatt.getDevice().getName()); if (Values.whoistop[0]) { UIUtils.sendBleMes("FOUND_DEVICES", "UNFOUNDED"); } //context.sendBroadcast(new Intent(BROADCAST_ACTION_CONNECTED_CHANGED));//傳送一個連線改變的廣播 /*if(BluetoothAdapter.getDefaultAdapter().getState() == BluetoothAdapter.STATE_ON){ close(); startScan(); }*/ } } @Override public void onServicesDiscovered(final BluetoothGatt gatt, int status) { LogUtils.i("onServicesDiscovered:" + gatt.getDevice().getName() + ", status:" + status + ", isConnected = " + isConnected()); if (isConnected()) { return; } isConnected = false; //connectingAddress.remove(getDevieNameAndAddress(gatt.getDevice())); if (status == BluetoothGatt.GATT_SUCCESS) {//A GATT operation completed successfully for (final BluetoothGattService server : gatt.getServices()) { if (!isConnected) { isConnected = isOwnerUUID(server.getUuid().toString());//判斷這個server是不是使用者定義的uuid,如果是就為真,只有這裡isConnected才為真 LogUtils.i("onServicesDiscovered:服務UUID匹配狀況:" + server.getUuid().toString() + ":連線狀況:" + isConnected); if (isConnected) { bluetoothGatt = gatt;//費勁千辛萬苦找到了gatt stopScan(); LogUtils.i("onServicesDiscovered:停止掃描,已連線到到裝置:" + gatt.getDevice().getName()); ShowToast.show(UIUtils.getContext(), "找到裝置:" + gatt.getDevice().getName()); if (Values.whoistop[0]) { UIUtils.sendBleMes("FOUND_DEVICES", gatt.getDevice().getName()); } setCharacteristicNotification(gatt, server, User_Definition_Characteristic_UUID); } //context.sendBroadcast(new Intent(BROADCAST_ACTION_CONNECTED_CHANGED));//也傳送一個連線改變的廣播 } //setCharacteristicNotification(gatt, server, Battery_Service_UUID);//設定電池 } } else { isConnected = false; //context.sendBroadcast(new Intent(BROADCAST_ACTION_CONNECTED_CHANGED)); LogUtils.i("onServicesDiscovered:Gatt未連線成功"); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); LogUtils.i("onCharacteristicChanged:" + characteristic.getUuid() + ", value=" + bytesToHexString(characteristic.getValue())); /*if (characteristic.getService().getUuid().toString().equals(Battery_Service_UUID)) { LogUtils.i("onCharacteristicChanged:Battery_Service_UUID:" + Battery_Service_UUID); //此處可以處理電池service資訊 return; }*/ //如果發過來的指令不包含在我們定義的keymap裡面,就不處理 if (uiHander == null || !KEY_MAP.containsKey(bytesToHexString(characteristic.getValue()))) { LogUtils.i("傳送的Characteristic指令不存在或者uiHander為空"); return; } //走到這裡說明是我們自己的鍵值對 Integer what = KEY_MAP.get(bytesToHexString(characteristic.getValue())); uiHander.removeMessages(what); uiHander.sendEmptyMessageDelayed(what, 100); } };
//開啟Notification,才能接受發過來的Characteristic資料 private void setCharacteristicNotification(BluetoothGatt gatt, BluetoothGattService server, String uuid) { BluetoothGattCharacteristic characteristic = server.getCharacteristic(UUID.fromString(uuid));//根據一個UUID,獲得一個服務下的Characteristic if (characteristic != null) { LogUtils.i("進入到setCharacteristicNotification() + " + uuid); gatt.setCharacteristicNotification(characteristic, true);} }
//停止掃描 public void stopScan() {BluetoothAdapter.getDefaultAdapter().stopLeScan(leScanCallback); }