安卓低功耗藍牙的使用

近期一個項目需要用到低功耗藍牙的開發(fā),由于之前沒有藍牙開發(fā)的經驗,發(fā)現(xiàn)網(wǎng)上關于藍牙開發(fā)的資料不多,不是隨便描述一下就是已經過時的,在此整理一篇低功耗藍牙的入門資料,能夠完成使用藍牙的接受和發(fā)送數(shù)據(jù)。

低功耗藍牙 (BLE,Bluetooth Low Energy的簡稱) 從Android 4.3 開始支持,如今越來越多外設都是使用低功耗藍牙來傳輸數(shù)據(jù)的,與經典藍牙本質上沒有太多的區(qū)別,有很多相似之處,工作流程都是:發(fā)現(xiàn)設備 --> 配對/綁定設備 --> 連接設備 --> 數(shù)據(jù)傳輸。但是,低功耗藍牙在安卓開發(fā)中的使用和經典藍牙是完全不同的,如果按照之前很熟悉的經典藍牙開發(fā)思維來做,說不定還會踩坑。。。

官方相關的開發(fā)指南:
經典藍牙
低功耗藍牙
低功耗藍牙使用實例項目

基本概念

先來了解一些關于低功耗藍牙的基本概念:

  • Generic Attribute Profile (GATT)——全稱叫做通用屬性配置文件,GATT按照層級定義了三個概念,服務(Service)、特征(Characteristic)和描述(Descriptor)。一個 Service 包含若干個 Characteristic,一個 Characteristic 包含若干個 Descriptor。
  • Characteristic——可以理解為一個類,包含了一個 value 和零至多個對該 value 的描述。
  • Descriptor——對 Characteristic 的描述,例如范圍和計量單位等。
  • Service——Characteristic的集合。

這些概念不用深入去探究,有一定了解開發(fā)的時候不至于一無所知就夠了,想要具體了解低功耗藍牙這里有篇不錯的文章

低功耗藍牙開發(fā)步驟

1.聲明權限

使用藍牙功能首先需要聲明相關的權限,比如:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

同時,也就可以通過藍牙特性配置來限制支持藍牙功能的設備使用APP:

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

或者通過在代碼中判斷,不過現(xiàn)在基本沒有什么手機不支持藍牙功能了吧。

// 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();
}

需要注意的是,官方說明 Android 5.0 及以上設備使用藍牙時還需要定位權限,需要注意的是開發(fā)的時候如果是在 Android 6.0 及以上設備的需要動態(tài)獲取定位權限,否則藍牙功能也是無法使用的。

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<manifest ... >
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
    <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
    <uses-feature android:name="android.hardware.location.gps" />
    ...
</manifest>
2.初始化藍牙適配器
private BluetoothAdapter mBluetoothAdapter;
...
// Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
        (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
3.開啟藍牙

在開始掃描發(fā)現(xiàn)藍牙設備之前需要確保手機的藍牙功能打開。

if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    // 申請打開藍牙
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

然后在 onActivityResultI方法中判斷用戶是否同意開啟藍牙功能。

4.發(fā)現(xiàn)設備
private static final long SCAN_PERIOD = 10000;
private void scanLeDevice(final boolean enable) {
      // Stops scanning after a pre-defined scan period.
      mHandler.postDelayed(new Runnable() {
          @Override
          public void run() {
              mScanning = false;
              mBluetoothAdapter.stopLeScan(mLeScanCallback);
          }
      }, SCAN_PERIOD);
      mScanning = true;
      mBluetoothAdapter.startLeScan(mLeScanCallback);
}

/**
* 發(fā)現(xiàn)設備的回調
*/
private BluetoothAdapter.LeScanCallback mLeScanCallback =
        new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
            byte[] scanRecord) {

    }
};
5.連接設備
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

連接設備的方法需要傳入三個參數(shù),第一個是 context,第二個是 boolean,表示是否自動連接,第三個是連接的回調接口,其中有幾個很重要的方法。

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                mConnectionState = STATE_CONNECTED;
                // 開始查找服務,只有找到服務才算是真的連接上
                mBluetoothGatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {

            } else {

            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
              byte[] data = characteristic.getValue();
        }
    };

連接設備成功后需要在回調方法中發(fā)現(xiàn)服務mBluetoothGatt.discoverServices(),發(fā)現(xiàn)服務后會回調onServicesDiscovered方法,發(fā)現(xiàn)服務成功才算是真正的連接上藍牙設備。onCharacteristicWrite方法是寫操作結果的回調,onCharacteristicChanged方法是狀態(tài)改變的回調,在該方法中能夠獲取藍牙發(fā)送的數(shù)據(jù)。不過,在接收數(shù)據(jù)之前,我們必須對Characteristic設置監(jiān)聽才能夠接收到藍牙的數(shù)據(jù)。

6.Characteristic監(jiān)聽設置
public void setNotification() {
 
    BluetoothGattService service = mBluetoothGatt.getService(SERVICE_UUID);
    if (service == null) {
        L.e("未找到藍牙中的對應服務");
        return;
    }
    BluetoothGattCharacteristic characteristic= service.getCharacteristic(CharacteristicUUID);
    if (characteristic== null) {
        L.e("未找到藍牙中的對應特征");
        return;
    }
    //設置true為啟用通知,false反之
    mBluetoothGatt.setCharacteristicNotification(characteristic, true);
 
    //下面為開啟藍牙notify功能,向CCCD中寫入值1
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD);
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    mBluetoothGatt.writeDescriptor(descriptor);
}

先通過 UUID 找到我們需要進行數(shù)據(jù)傳輸?shù)膕ervice,在找到我們想要設置監(jiān)聽的Characteristic的 UUID 找到響應的characteristic對象,找到響應的characteristic后調用setCharacteristicNotification方法啟用通知,則該characteristic狀態(tài)發(fā)生改變后就會回調onCharacteristicChanged方法,而開啟藍牙的 notify 功能需要向 UUID 為 CCCD 的 descriptor 中寫入值1,其中 CCCD 的值為:

public static final UUID CCCD = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
7.向藍牙發(fā)送數(shù)據(jù)

往藍牙發(fā)送數(shù)據(jù),可以理解為給藍牙的characteristic設置數(shù)據(jù)。

public void writeRXCharacteristic(byte[] value) {
        if (mBluetoothGatt == null) {
            return;
        }
        BluetoothGattService service= mBluetoothGatt.getService(SERVICE_UUID);
        BluetoothGattCharacteristic characteristic= service.getCharacteristic(UUID);
        characteristic.setValue(value);
        mBluetoothGatt.writeCharacteristic(characteristic);
    }

我們可以寫入Stringbyte[]的數(shù)據(jù),一般為byte[]。其中我們需要與哪個service的characteristic進行數(shù)據(jù)傳輸可以聯(lián)系硬件工程師或者查看藍牙設備供應商提供的說明獲得。我們也可以通過mBluetoothGatt.getServices()mBluetoothGatt.getgetCharacteristics()方法獲取藍牙設備的所有
service 和某個 service 中的所有 characteristic。

8.數(shù)據(jù)分包處理

低功耗藍牙一次性只能發(fā)送 20 個字節(jié)的數(shù)據(jù),超過 20 個字節(jié)的無法發(fā)送,因此需要對發(fā)送的數(shù)據(jù)進行分包處理,在此給出數(shù)據(jù)分包的一個示例,是在別人 github 中看到的:

//存儲待發(fā)送的數(shù)據(jù)隊列
    private Queue<byte[]> dataInfoQueue = new LinkedList<>();
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            send();
        }
    };

    /**
     * 向characteristic寫數(shù)據(jù)
     *
     * @param value
     */
    public void writeCharacteristic(BluetoothGattCharacteristic characteristic, byte[] value) {
        this.mCharacteristic = characteristic;
        if (dataInfoQueue != null) {
            dataInfoQueue.clear();
            dataInfoQueue = splitPacketFor20Byte(value);
            handler.post(runnable);
        }
        // characteristic.setValue(value);
        // mBluetoothGatt.writeCharacteristic(characteristic);
    }

    private void send() {
        if (dataInfoQueue != null && !dataInfoQueue.isEmpty()) {
            //檢測到發(fā)送數(shù)據(jù),直接發(fā)送
            if (dataInfoQueue.peek() != null) {
                this.mCharacteristic.setValue(dataInfoQueue.poll());//移除并返回隊列頭部的元素
                mBluetoothGatt.writeCharacteristic(mCharacteristic);
            }
            //檢測還有數(shù)據(jù),延時后繼續(xù)發(fā)送,一般延時100毫秒左右
            if (dataInfoQueue.peek() != null) {
                handler.postDelayed(runnable, 200);
            }
        }
    }

    //數(shù)據(jù)分包處理
    private Queue<byte[]> splitPacketFor20Byte(byte[] data) {
        Queue<byte[]> dataInfoQueue = new LinkedList<>();
        if (data != null) {
            int index = 0;
            do {
                byte[] surplusData = new byte[data.length - index];
                byte[] currentData;
                System.arraycopy(data, index, surplusData, 0, data.length - index);
                if (surplusData.length <= 20) {
                    currentData = new byte[surplusData.length];
                    System.arraycopy(surplusData, 0, currentData, 0, surplusData.length);
                    index += surplusData.length;
                } else {
                    currentData = new byte[20];
                    System.arraycopy(data, index, currentData, 0, 20);
                    index += 20;
                }
                dataInfoQueue.offer(currentData);
            } while (index < data.length);
        }
        return dataInfoQueue;
    }

這篇文章簡單介紹了安卓進行低功耗藍牙開發(fā)的流程以及提到了一些注意事項,看完本文基本就能夠進行低功耗藍牙開發(fā),除此以外,強烈推薦去 Github 看一下低功耗藍牙使用實例項目,例子不難理解,看懂了 Demo 能對低功耗藍牙的使用有更深的了解。

本文原文地址:Android 低功耗藍牙開發(fā)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • BLE 與經典藍牙的區(qū)別 BLE 的 Kotlin 下實踐 BluetoothGattCallback 不回調異常...
    chauI閱讀 11,596評論 1 7
  • 初識低功耗藍牙 Android 4.3(API Level 18)開始引入Bluetooth Low Energy...
    JBD閱讀 113,571評論 46 342
  • 藍牙 藍牙的波段為2400-2483.5MHz(包括防護頻帶)。這是全球范圍內無需取得執(zhí)照(但定不是無管制的)的工...
    蘇永茂閱讀 6,590評論 0 11
  • 我愿意回到起點去做自己喜歡的事情。
    螢火蟲的夢想閱讀 82評論 0 0
  • 即使我已經洞察周易的結果 但也只愿意 和你重新來過 我想把所有的乖戾殘忍猙獰瘋狂傾灑給世界 因為那樣 給你的 就只...
    周荀川閱讀 734評論 0 2

友情鏈接更多精彩內容