目錄
前言
之前的幾篇文章,主要介紹了經典藍牙開發相關的知識,今天我們就來看看低功耗藍牙的開發。如果小夥伴們對之前的文章感興趣興趣,也可以看看,歡迎提出不足或者建議。
【Android】藍牙開發——經典藍牙配對介紹(通過手機系統藍牙演示)
【Android】藍牙開發—— 經典藍牙配對介紹(Java代碼實現演示)附Demo源碼
一、相關概念介紹
BLE,全稱 Bluetooth Low Energy,即低功耗藍牙。BLE關鍵術語和概念,可以查看官網介紹:
https://developer.android.google.cn/guide/topics/connectivity/bluetooth-le
首先,我們需要知道,BLE設備有以下幾個方面的東西:
1、一個BLE設備包含多個服務,這些服務通過UUID來區分。
2、一個服務包含1個或多個特徵,這些特徵也是通過UUID來區分。
3、一個特徵包含一個Value和多個描述符,一個描述符包含一個Value,
其次,我們來看一看Android中BLE相關的對象:
1、BluetoothDevice :藍牙設備
2、BluetoothGatt:建立的連接
3、BluetoothGattCallback:連接回調
3、BluetoothGattService:服務
4、BluetoothGattCharacteristic:服務的特徵
5、BluetoothGattDescriptor:特徵的描述
瞭解了這些之後,我們開始進入BLE開發階段。
二、實戰開發
注意:BLE是在Android 4.3(API 18)以後引入的,所以要進行BLE開發,必須在Android 4.3以上版本的機子上。
1、添加相關權限
(1)藍牙權限
<!-- 應用使用藍牙的權限 --> <uses-permission android:name="android.permission.BLUETOOTH"/> <!--啓動設備發現或操作藍牙設置的權限--> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
(2)位置權限(BLE與經典藍牙相比,還需要位置權限,如果沒有,有的機型是掃描不到設備的,注意Android6.0以後還需要動態申請位置權限)
<!--位置權限--> <!--Android 10以上系統,需要ACCESS_FINE_LOCATION--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <!--Android 9以及以下系統,需要ACCESS_FINE_LOCATION--> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
2、使用藍牙之前,首先要檢查當前手機是否支持BLE藍牙。如果支持BLE藍牙,檢查手機藍牙是否已開啓。如果沒有開啓,則需要先打開藍牙。打開手機藍牙,有兩種方式,一種是直接enable()打開,另外一種是提示用戶打開,推薦第二種方式。
/** * 檢測手機是否支持4.0藍牙 * @param context 上下文 * @return true--支持4.0 false--不支持4.0 */ private boolean checkBle(Context context){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { //API 18 Android 4.3 bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); if(bluetoothManager == null){ return false; } bluetooth4Adapter = bluetoothManager.getAdapter(); //BLUETOOTH權限 if(bluetooth4Adapter == null){ return false; }else{ Log.d(TAG,"該設備支持藍牙4.0"); return true; } }else{ return false; } }
/** * 獲取藍牙狀態 */ public boolean isEnable(){ if(bluetooth4Adapter == null){ return false; } return bluetooth4Adapter.isEnabled(); } /** * 打開藍牙 * @param isFast true 直接打開藍牙 false 提示用戶打開 */ public void openBluetooth(Context context,boolean isFast){ if(!isEnable()){ if(isFast){ Log.d(TAG,"直接打開手機藍牙"); bluetooth4Adapter.enable(); //BLUETOOTH_ADMIN權限 }else{ Log.d(TAG,"提示用戶去打開手機藍牙"); Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); context.startActivity(enableBtIntent); } }else{ Log.d(TAG,"手機藍牙狀態已開"); } }
3、系統藍牙已打開,則可以開啓掃描設備。需要注意的是,掃描設備是耗時的操作,一旦掃描結束,就要及時停止掃描。
//////////////////////////////////// 掃描設備 /////////////////////////////////////////////// //掃描設備回調 @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] bytes) { //在onLeScan()回調中儘量做少的操作,可以將掃描到的設備扔到另一個線程中處理 if(bluetoothDevice == null) return; if(bluetoothDevice.getName() != null){ Log.d(TAG,bluetoothDevice.getName() + "-->" + bluetoothDevice.getAddress()); }else{ Log.d(TAG,"null" + "-->" + bluetoothDevice.getAddress()); } BLEDevice bleDevice = new BLEDevice(bluetoothDevice,rssi); if(onDeviceSearchListener != null){ onDeviceSearchListener.onDeviceFound(bleDevice); //掃描到設備回調 } } }; /** * 設置時間段 掃描設備 * @param onDeviceSearchListener 設備掃描監聽 * @param scanTime 掃描時間 */ public void startDiscoveryDevice(OnDeviceSearchListener onDeviceSearchListener,long scanTime){ if(bluetooth4Adapter == null){ Log.e(TAG,"startDiscoveryDevice-->bluetooth4Adapter == null"); return; } this.onDeviceSearchListener = onDeviceSearchListener; if(onDeviceSearchListener != null){ onDeviceSearchListener.onDiscoveryStart(); //開始掃描回調 } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { Log.d(TAG,"開始掃描設備"); bluetooth4Adapter.startLeScan(leScanCallback); }else{ return; } //設定最長掃描時間 mHandler.postDelayed(stopScanRunnable,scanTime); } private Runnable stopScanRunnable = new Runnable() { @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public void run() { if(onDeviceSearchListener != null){ onDeviceSearchListener.onDiscoveryOutTime(); //掃描超時回調 } //scanTime之後還沒有掃描到設備,就停止掃描。 stopDiscoveryDevice(); } };
4、掃描到目標設備之後,開始建立連接,這裏注意,連接之前一定要關閉掃描,否則會影響連接。BLE與經典藍牙不同,經典藍牙一旦建立連接,就可以進行數據通訊,而BLE建立連接之後,還需要發現系統服務,獲取特定服務及讀寫特徵。
(1)建立連接 & 發現系統服務
///////////////////////////////////// 執行連接 ////////////////////////////////////////////// //連接/通訊結果回調 @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { @Override public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { super.onPhyUpdate(gatt, txPhy, rxPhy, status); } @Override public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { super.onPhyRead(gatt, txPhy, rxPhy, status); } //連接狀態回調-連接成功/斷開連接 @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); Log.d(TAG,"status:" + status); Log.d(TAG,"newState:" + newState); switch(status){ case BluetoothGatt.GATT_SUCCESS: Log.w(TAG,"BluetoothGatt.GATT_SUCCESS"); break; case BluetoothGatt.GATT_FAILURE: Log.w(TAG,"BluetoothGatt.GATT_FAILURE"); break; case BluetoothGatt.GATT_CONNECTION_CONGESTED: Log.w(TAG,"BluetoothGatt.GATT_CONNECTION_CONGESTED"); break; case BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION: Log.w(TAG,"BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION"); break; case BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION: Log.w(TAG,"BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION"); break; case BluetoothGatt.GATT_INVALID_OFFSET: Log.w(TAG,"BluetoothGatt.GATT_INVALID_OFFSET"); break; case BluetoothGatt.GATT_READ_NOT_PERMITTED: Log.w(TAG,"BluetoothGatt.GATT_READ_NOT_PERMITTED"); break; case BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED: Log.w(TAG,"BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED"); break; } BluetoothDevice bluetoothDevice = gatt.getDevice(); Log.d(TAG,"連接的設備:" + bluetoothDevice.getName() + " " + bluetoothDevice.getAddress()); isConnectIng = false; //移除連接超時 mHandler.removeCallbacks(connectOutTimeRunnable); if(newState == BluetoothGatt.STATE_CONNECTED){ Log.w(TAG,"連接成功"); //連接成功去發現服務 gatt.discoverServices(); //設置發現服務超時時間 mHandler.postDelayed(serviceDiscoverOutTimeRunnable,MAX_CONNECT_TIME); if(onBleConnectListener != null){ onBleConnectListener.onConnectSuccess(gatt,bluetoothDevice,status); //連接成功回調 } }else if(newState == BluetoothGatt.STATE_DISCONNECTED) { //清空系統緩存 ClsUtils.refreshDeviceCache(gatt); Log.e(TAG, "斷開連接status:" + status); gatt.close(); //斷開連接釋放連接 if(status == 133){ //無法連接 if(onBleConnectListener != null){ onBleConnectListener.onConnectFailure(gatt,bluetoothDevice,"連接異常!",status); //133連接異常 異常斷開 Log.e(TAG,"連接失敗status:" + status + " " + bluetoothDevice.getAddress()); } }else if(status == 62){ //成功連接沒有發現服務斷開 if(onBleConnectListener != null){ onBleConnectListener.onConnectFailure(gatt,bluetoothDevice,"連接成功服務未發現斷開!",status); //62沒有發現服務 異常斷開 Log.e(TAG,"連接成功服務未發現斷開status:" + status); } }else if(status == 0){ if(onBleConnectListener != null){ onBleConnectListener.onDisConnectSuccess(gatt,bluetoothDevice,status); //0正常斷開 回調 } }else if(status == 8){ //因爲距離遠或者電池無法供電斷開連接 // 已經成功發現服務 if(onBleConnectListener != null){ onBleConnectListener.onDisConnectSuccess(gatt,bluetoothDevice,status); //8斷電斷開 回調 } }else if(status == 34){ if(onBleConnectListener != null){ onBleConnectListener.onDisConnectSuccess(gatt,bluetoothDevice,status); //34斷開 } }else { //其它斷開連接 if(onBleConnectListener != null){ onBleConnectListener.onDisConnectSuccess(gatt,bluetoothDevice,status); //其它斷開 } } }else if(newState == BluetoothGatt.STATE_CONNECTING){ Log.d(TAG,"正在連接..."); if(onBleConnectListener != null){ onBleConnectListener.onConnecting(gatt,bluetoothDevice); //正在連接回調 } }else if(newState == BluetoothGatt.STATE_DISCONNECTING){ Log.d(TAG,"正在斷開..."); if(onBleConnectListener != null){ onBleConnectListener.onDisConnecting(gatt,bluetoothDevice); //正在斷開回調 } } } //發現服務 @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); //移除發現服務超時 mHandler.removeCallbacks(serviceDiscoverOutTimeRunnable); Log.d(TAG,"移除發現服務超時"); Log.d(TAG,"發現服務"); //獲取特定服務及特徵 if(setupService(gatt,serviceUUID,readUUID,writeUUID)){ if(onBleConnectListener != null){ onBleConnectListener.onServiceDiscoverySucceed(gatt,gatt.getDevice(),status); //成功發現服務回調 } }else{ if(onBleConnectListener != null){ onBleConnectListener.onServiceDiscoveryFailed(gatt,gatt.getDevice(),"獲取服務特徵異常"); //發現服務失敗回調 } } } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); Log.d(TAG,"讀status: " + status); } //向藍牙設備寫入數據結果回調 @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if(characteristic.getValue() == null){ Log.e(TAG,"characteristic.getValue() == null"); return; } //將收到的字節數組轉換成十六進制字符串 String msg = TypeConversion.bytes2HexString(characteristic.getValue(),characteristic.getValue().length); if(status == BluetoothGatt.GATT_SUCCESS){ //寫入成功 Log.w(TAG,"寫入成功:" + msg); if(onBleConnectListener != null){ onBleConnectListener.onWriteSuccess(gatt,gatt.getDevice(),characteristic.getValue()); //寫入成功回調 } }else if(status == BluetoothGatt.GATT_FAILURE){ //寫入失敗 Log.e(TAG,"寫入失敗:" + msg); if(onBleConnectListener != null){ onBleConnectListener.onWriteFailure(gatt,gatt.getDevice(),characteristic.getValue(),"寫入失敗"); //寫入失敗回調 } }else if(status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED){ //沒有權限 Log.e(TAG,"沒有權限!"); } } //讀取藍牙設備發出來的數據回調 @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); //接收數據 byte[] bytes = characteristic.getValue(); Log.w("TAG","收到數據str:" + TypeConversion.bytes2HexString(bytes,bytes.length)); if(onBleConnectListener != null){ onBleConnectListener.onReceiveMessage(gatt,gatt.getDevice(),characteristic,characteristic.getValue()); //接收數據回調 } } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); } @Override public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { super.onReliableWriteCompleted(gatt, status); Log.d(TAG,"onReliableWriteCompleted"); } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status); if(status == BluetoothGatt.GATT_SUCCESS){ Log.w(TAG,"讀取RSSI值成功,RSSI值:" + rssi + ",status" + status); if(onBleConnectListener != null){ onBleConnectListener.onReadRssi(gatt,rssi,status); //成功讀取連接的信號強度回調 } }else if(status == BluetoothGatt.GATT_FAILURE){ Log.w(TAG,"讀取RSSI值失敗,status:" + status); } } //修改MTU值結果回調 @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); ///設置mtu值,即bluetoothGatt.requestMtu()時觸發,提示該操作是否成功 if(status == BluetoothGatt.GATT_SUCCESS){ //設置MTU成功 //MTU默認取的是23,當收到 onMtuChanged 後,會根據傳遞的值修改MTU,注意由於傳輸用掉3字節,因此傳遞的值需要減3。 //mtu - 3 Log.w(TAG,"設置MTU成功,新的MTU值:" + (mtu-3) + ",status" + status); if(onBleConnectListener != null){ onBleConnectListener.onMTUSetSuccess("設置後新的MTU值 = " + (mtu-3) + " status = " + status,mtu - 3); //MTU設置成功 } }else if(status == BluetoothGatt.GATT_FAILURE){ //設置MTU失敗 Log.e(TAG,"設置MTU值失敗:" + (mtu-3) + ",status" + status); if(onBleConnectListener != null){ onBleConnectListener.onMTUSetFailure("設置MTU值失敗:" + (mtu-3) + " status:" + status); //MTU設置失敗 } } } }; /** * 通過藍牙設備連接 * @param context 上下文 * @param bluetoothDevice 藍牙設備 * @param outTime 連接超時時間 * @param onBleConnectListener 藍牙連接監聽者 * @return */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public BluetoothGatt connectBleDevice(Context context, BluetoothDevice bluetoothDevice, long outTime,OnBleConnectListener onBleConnectListener){ if(bluetoothDevice == null){ Log.e(TAG,"addBLEConnectDevice-->bluetoothDevice == null"); return null; } this.onBleConnectListener = onBleConnectListener; this.curConnDevice = bluetoothDevice; Log.d(TAG,"開始準備連接:" + bluetoothDevice.getName() + "-->" + bluetoothDevice.getAddress()); //出現 BluetoothGatt.android.os.DeadObjectException 藍牙沒有打開 try{ mBluetoothGatt = bluetoothDevice.connectGatt(context,false,bluetoothGattCallback); mBluetoothGatt.connect(); }catch(Exception e){ Log.e(TAG,"e:" + e.getMessage()); } mHandler.postDelayed(connectOutTimeRunnable,outTime); return mBluetoothGatt; } //連接超時 private Runnable connectOutTimeRunnable = new Runnable() { @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public void run() { if(mBluetoothGatt == null){ Log.e(TAG,"connectOuttimeRunnable-->mBluetoothGatt == null"); return; } isConnectIng = false; mBluetoothGatt.disconnect(); //連接超時當作連接失敗回調 if(onBleConnectListener != null){ onBleConnectListener.onConnectFailure(mBluetoothGatt,curConnDevice,"連接超時!",-1); //連接失敗回調 } } }; //發現服務超時 private Runnable serviceDiscoverOutTimeRunnable = new Runnable() { @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public void run() { if(mBluetoothGatt == null){ Log.e(TAG,"connectOuttimeRunnable-->mBluetoothGatt == null"); return; } isConnectIng = false; mBluetoothGatt.disconnect(); //發現服務超時當作連接失敗回調 if(onBleConnectListener != null){ onBleConnectListener.onConnectFailure(mBluetoothGatt,curConnDevice,"發現服務超時!",-1); //連接失敗回調 } } };
(2)發現系統服務之後,還需要獲取特定服務及讀寫特徵才能進行數據通訊。一般,讀特徵是用來讀取藍牙設備發出來的數據,寫特徵是向藍牙設備寫入數據,其中,讀特徵一定要設置打開通知,否則接收不到消息。
/** * 獲取特定服務及特徵 * 1個serviceUUID -- 1個readUUID -- 1個writeUUID * @param bluetoothGatt * @param serviceUUID * @param readUUID * @param writeUUID * @return */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) private boolean setupService(BluetoothGatt bluetoothGatt,String serviceUUID,String readUUID,String writeUUID) { if (bluetoothGatt == null) { Log.e(TAG, "setupService()-->bluetoothGatt == null"); return false; } if(serviceUUID == null){ Log.e(TAG, "setupService()-->serviceUUID == null"); return false; } for (BluetoothGattService service : bluetoothGatt.getServices()) { // Log.d(TAG, "service = " + service.getUuid().toString()); if (service.getUuid().toString().equals(serviceUUID)) { bluetoothGattService = service; } } //通過上面方法獲取bluetoothGattService // bluetoothGattService = bleManager.getBluetoothGattService(bluetoothGatt,ConsData.MY_BLUETOOTH4_UUID); if (bluetoothGattService == null) { Log.e(TAG, "setupService()-->bluetoothGattService == null"); return false; } Log.d(TAG, "setupService()-->bluetoothGattService = " + bluetoothGattService.toString()); if(readUUID == null || writeUUID == null){ Log.e(TAG, "setupService()-->readUUID == null || writeUUID == null"); return false; } for (BluetoothGattCharacteristic characteristic : bluetoothGattService.getCharacteristics()) { if (characteristic.getUuid().toString().equals(readUUID)) { //讀特徵 readCharacteristic = characteristic; } else if (characteristic.getUuid().toString().equals(writeUUID)) { //寫特徵 writeCharacteristic = characteristic; } } if (readCharacteristic == null) { Log.e(TAG, "setupService()-->readCharacteristic == null"); return false; } if (writeCharacteristic == null) { Log.e(TAG, "setupService()-->writeCharacteristic == null"); return false; } //打開讀通知 enableNotification(true, bluetoothGatt, readCharacteristic); //重點中重點,需要重新設置 List<BluetoothGattDescriptor> descriptors = writeCharacteristic.getDescriptors(); for (BluetoothGattDescriptor descriptor : descriptors) { descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); bluetoothGatt.writeDescriptor(descriptor); } //延遲2s,保證所有通知都能及時打開 mHandler.postDelayed(new Runnable() { @Override public void run() { } }, 2000); return true; } ///////////////////////////////////////// 打開通知 ////////////////////////////////////////// /** * 設置讀特徵接收通知 * @param enable 爲true打開通知 * @param gatt 連接 * @param characteristic 特徵 */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public void enableNotification(boolean enable, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic){ if(gatt == null){ Log.e(TAG,"enableNotification-->gatt == null"); return; } if(characteristic == null){ Log.e(TAG,"enableNotification-->characteristic == null"); return; } //這一步必須要有,否則接收不到通知 gatt.setCharacteristicNotification(characteristic,enable); }
5、數據通訊
(1)發送數據
mBluetoothGatt.writeCharacteristic()方法的返回值,並不能真正的表示數據是否發送成功,而是通過BluetoothGattCallback回調方法onCharacteristicWrite()來判斷數據是否已成功寫入底層。
//向藍牙設備寫入數據結果回調 @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if(characteristic.getValue() == null){ Log.e(TAG,"characteristic.getValue() == null"); return; } //將收到的字節數組轉換成十六進制字符串 String msg = TypeConversion.bytes2HexString(characteristic.getValue(),characteristic.getValue().length); if(status == BluetoothGatt.GATT_SUCCESS){ //寫入成功 Log.w(TAG,"寫入成功:" + msg); if(onBleConnectListener != null){ onBleConnectListener.onWriteSuccess(gatt,gatt.getDevice(),characteristic.getValue()); //寫入成功回調 } }else if(status == BluetoothGatt.GATT_FAILURE){ //寫入失敗 Log.e(TAG,"寫入失敗:" + msg); if(onBleConnectListener != null){ onBleConnectListener.onWriteFailure(gatt,gatt.getDevice(),characteristic.getValue(),"寫入失敗"); //寫入失敗回調 } }else if(status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED){ //沒有權限 Log.e(TAG,"沒有權限!"); } }
/////////////////////////////////// 發送數據 /////////////////////////////////////////////// /** * 發送消息 byte[]數組 * @param msg 消息 * @return true false */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public boolean sendMessage( byte[] msg){ if(writeCharacteristic == null){ Log.e(TAG,"sendMessage(byte[])-->writeGattCharacteristic == null"); return false; } if(mBluetoothGatt == null){ Log.e(TAG,"sendMessage(byte[])-->mBluetoothGatt == null"); return false; } boolean b = writeCharacteristic.setValue(msg); Log.d(TAG, "寫特徵設置值結果:" + b); return mBluetoothGatt.writeCharacteristic(writeCharacteristic); }
(2)接收數據
接收的數據是直接通過BluetoothGattCallback回調方法onCharacteristicChanged()來獲取的。
//讀取藍牙設備發出來的數據回調 @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); //接收數據 byte[] bytes = characteristic.getValue(); Log.w("TAG","收到數據str:" + TypeConversion.bytes2HexString(bytes,bytes.length)); if(onBleConnectListener != null){ onBleConnectListener.onReceiveMessage(gatt,gatt.getDevice(),characteristic,characteristic.getValue()); //接收數據回調 } }
6、斷開連接
BLE通訊結束之後,需要及時斷開連接,並且在斷開連接的回調處釋放資源。否則會導致下一次執行連接操作時,導致133異常。所以,一般連接出現133異常,都是因爲斷開後及時釋放資源。
斷開連接的結果是在BluetoothGattCallback回調方法onConnectionStateChange()來獲取的。(可查看上面建立連接處的代碼)
/////////////////////////////////// 斷開連接 /////////////////////////////////////////////// /** * 斷開連接 */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public void disConnectDevice(){ if(mBluetoothGatt == null){ Log.e(TAG,"disConnectDevice-->bluetoothGatt == null"); return; } //系統斷開 mBluetoothGatt.disconnect(); }
三、項目演示
(1)掃描目標設備(BLEyqy),點擊“連接”按鈕, 會在“搜索”按鈕下方顯示連接結果 。
(2)手機給目標設備發送數據成功之後,藍牙設備把接收到的數據再回發送給手機。
(3)斷開連接。點擊“斷開”按鈕, 會在“搜索”按鈕下方顯示斷開結果 。
四、Demo案例源碼地址
碼雲:https://gitee.com/lilium_foliage/Android-Bluetooth-Low-Energy
CSDN:https://download.csdn.net/download/qq_38950819/12001139