【Android】藍牙開發——BLE(低功耗藍牙)(附完整Demo)

目錄

 

前言

一、相關概念介紹

二、實戰開發

三、項目演示

四、Demo案例源碼地址


前言

之前的幾篇文章,主要介紹了經典藍牙開發相關的知識,今天我們就來看看低功耗藍牙的開發。如果小夥伴們對之前的文章感興趣興趣,也可以看看,歡迎提出不足或者建議。

【Android】藍牙開發——經典藍牙(附Demo源碼)

【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),點擊“連接”按鈕, 會在“搜索”按鈕下方顯示連接結果 。

BLE掃描連接

(2)手機給目標設備發送數據成功之後,藍牙設備把接收到的數據再回發送給手機。

BLE發送接收數據

(3)斷開連接。點擊“斷開”按鈕, 會在“搜索”按鈕下方顯示斷開結果 。

BLE斷開連接

四、Demo案例源碼地址

碼雲:https://gitee.com/lilium_foliage/Android-Bluetooth-Low-Energy

CSDN:https://download.csdn.net/download/qq_38950819/12001139

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章