Android開發總結之低功耗藍牙開發

前言

因筆者工作需要,開發一款藍牙秤的數據讀取軟件。有些心得,便在此記錄。俗話說:好記性不如爛筆頭。筆者也是爲了備忘。寫下本篇博客。幫助有需要的同學~

本篇文章來源於開發者指導翻譯,開發者指導示例代碼。以及自己的一些心得。幫助不會藍牙低功耗的同學快速入門。

本篇博客有大多數都是官網直譯,小部分爲自己心得。如有翻譯出錯地方,及文章錯誤。望衆大神在評論區指出。筆者定勤加修改

低功耗藍牙概述

Android 4.3(API級別18)引入了內置平臺支持藍牙低功耗(BLE)的核心角色,並提供應用程序可用於發現設備,查詢服務和傳輸信息的API。

常見用例包括以下內容:

  • 在附近設備之間傳輸少量數據。
  • 與Google Beacons等接近傳感器進行交互,根據用戶的當前位置爲用戶提供定製體驗。

與傳統藍牙相比,藍牙低功耗(BLE)旨在提供顯着降低的功耗。 這允許Android應用程序與具有更嚴格電源要求的BLE設備通信,例如接近傳感器,心率監視器和健身設備。

關鍵術語和概念

以下是關鍵BLE術語和概念的摘要:

  • Generic Attribute Profile (GATT) ——GATT配置文件是用於在BLE鏈路上發送和接收稱爲“屬性”的短數據的通用規範。 目前所有低能耗應用配置文件均基於GATT。

  • Bluetooth SIG爲低能耗設備定義了許多配置文件。 配置文件是設備在特定應用程序中的工作方式的規範。 請注意,設備可以實現多個配置文件。 例如,設備可以包含心率監測器和電池水平檢測器。

  • Attribute Protocol (ATT) ——GATT建立在屬性協議(ATT)之上。 這也稱爲GATT / ATT。 ATT經過優化,可在BLE設備上運行。 爲此,它使用儘可能少的字節。 每個屬性由通用唯一標識符(UUID)唯一標識,UUID是用於唯一標識信息的字符串ID的標準化128位格式。 ATT傳輸的屬性被格式化爲特徵和服務。

  • Characteristic——特徵包含單個值和描述特徵值的0-n描述符。 特徵可以被認爲是類型,類似於類。

  • Descriptor——描述符是定義描述特徵值的屬性。 例如,描述符可以指定人類可讀的描述,特徵值的可接受範圍,或特徵值特定的度量單位。

  • Service——服務是一系列特徵。 例如,您可以使用名爲“心率監測器”的服務,其中包括“心率測量”等特徵。 您可以在bluetooth.org上找到基於GATT的現有配置文件和服務的列表。

角色和責任

以下是Android設備與BLE設備交互時應用的角色和職責:

  • 中央與外圍(Central vs. peripheral)。 這適用於BLE連接本身。 中心角色的設備掃描,尋找廣告,外圍角色的設備製作廣告。
  • GATT服務器與GATT客戶端。 這確定了兩個設備建立連接後如何相互通信。

低功耗藍牙權限

要在您的應用程序中使用藍牙功能,您必須聲明藍牙權限BLUETOOTH。 您需要此權限才能執行任何藍牙通信,例如請求連接,接受連接和傳輸數據。

如果您希望應用程序啓動設備發現或操作藍牙設置,則還必須聲明BLUETOOTH_ADMIN權限。 注意:如果使用BLUETOOTH_ADMIN權限,則還必須具有BLUETOOTH權限。

在應用程序清單文件中聲明藍牙權限。 例如:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!--如果需要搜索藍牙設備,需要加上定位權限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

如果您要聲明您的應用僅適用於支持BLE的設備,請在應用清單中包含以下內容:

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

但是,如果您希望將應用程序提供給不支持BLE的設備,您仍應將此元素包含在應用程序的清單中,但必須設置required =“false”。 然後在運行時,您可以使用PackageManager.hasSystemFeature()確定BLE可用性:

// 使用此檢查確定設備是否支持BLE。 然後,您可以有選擇地禁用與BLE相關的功能。
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}

注意:LE Beacons通常與位置相關聯。 要使用BluetoothLeScanner,您必須通過在應用程序的清單文件中聲明ACCESS_COARSE_LOCATION或ACCESS_FINE_LOCATION權限來請求用戶的權限。 沒有這些權限,掃描將不會返回任何結果。

設置低功耗藍牙

在您的應用程序可以通過BLE進行通信之前,您需要驗證設備是否支持BLE,如果是,請確保它已啓用。 請注意,僅當<uses-feature … />設置爲false時才需要進行此檢查。

如果不支持BLE,則應優雅地禁用任何BLE功能。 如果BLE受支持但已禁用,則您可以請求用戶啓用藍牙而無需離開您的應用程序。 使用BluetoothAdapter,可以分兩步完成此設置。

  • 獲取BluetoothAdapter
    任何和所有藍牙活動都需要BluetoothAdapter。 BluetoothAdapter代表設備自己的藍牙適配器(藍牙無線電)。 整個系統都有一個藍牙適配器,您的應用程序可以使用此對象與其進行交互。 下面的代碼段顯示瞭如何獲取適配器。 請注意,此方法使用getSystemService()返回BluetoothManager的實例,然後使用該實例獲取適配器。 Android 4.3(API Level 18)介紹了BluetoothManager:
private BluetoothAdapter bluetoothAdapter;

final BluetoothManager bluetoothManager =
        (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
  • 啓用藍牙
    接下來,您需要確保啓用藍牙。 調用isEnabled()檢查當前是否啓用了藍牙。 如果此方法返回false,則禁用藍牙。 以下代碼段會檢查是否已啓用藍牙。 如果不是,則代碼段會顯示錯誤提示用戶轉到“設置”以啓用藍牙:
// 確保設備上可以使用藍牙並啓用藍牙。 如果沒有,則顯示一個對話框,請求用戶啓用藍牙權限。
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

注意:傳遞給startActivityForResult(android.content.Intent,int)的REQUEST_ENABLE_BT常量是一個本地定義的整數(必須大於0),系統會在你的onActivityResult(int,int,android.content)中傳回給你。 Intent)實現爲requestCode參數。

找到低功耗設備

要查找BLE設備,請使用BluetoothLeScanner.startScan(ScanCallback)方法。 此方法將ScanCallback作爲參數。 您必須實現此回調,因爲這是返回掃描結果的方式。 由於掃描是非常浪費資源的,因此您應遵守以下準則:

  • 找到所需設備後,請立即停止掃描。
  • 切勿循環掃描,並設置掃描時間限制。 之前可用的設備可能已超出範圍,繼續掃描會消耗資源。

以下代碼段顯示瞭如何啓動和停止掃描:

/**
 * 停止藍牙掃描
 */
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public void stopScanLeDevice(ScanCallback scanCallback) {
    if (isEnable && mBluetoothAdapter.isEnabled()) {
        BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
        bluetoothLeScanner.stopScan(scanCallback);
    }
}

/**
 * 開始藍牙掃描
 */
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public void scanLeDevice(ScanCallback scanCallback) {
    if (isEnable && mBluetoothAdapter.isEnabled()) {
        BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
        bluetoothLeScanner.startScan(scanCallback);
    }
}


以下是ScanCallback的實現,它是用於提供BLE掃描結果的接口:

    private ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            result.getDevice();//藍牙設備
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            super.onBatchScanResults(results);
            L.e("批次掃描結果-onBatchScanResults");
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
            L.e("掃描失敗:onScanFailed-errorCode:" + errorCode);
        }
    };

連接到GATT 服務

與BLE設備交互的第一步是連接到它 - 更具體地說,連接到設備上的GATT服務器。 要連接到BLE設備上的GATT服務器,請使用connectGatt()方法。 此方法有三個參數:一個Context對象,autoConnect(指示是否在可用時自動連接到BLE設備),以及對BluetoothGattCallback的引用:

mBluetoothGatt = device.connectGatt(mContext, true, mBluetoothGattCallback);

這將連接到BLE設備託管的GATT服務器,並返回一個BluetoothGatt實例,然後您可以使用該實例執行GATT客戶端操作。 呼叫者(Android應用)是GATT客戶端。 BluetoothGattCallback用於向客戶端提供結果,例如連接狀態,以及任何進一步的GATT客戶端操作。

Gatt服務器狀態改變會調用監聽器中的回調函數:

private final BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        L.e("===========\t連接狀態改變\t===========");
        if (newState == BluetoothProfile.STATE_CONNECTING) {
            L.e("-----------\t連接中....\t-----------");
        } else if (newState == BluetoothProfile.STATE_CONNECTED) {
            L.e("連接到GATT服務器\n啓動搜索GATT服務器存在的服務:" +
                    (mBluetoothGatt.discoverServices() ? "啓動成功" : "啓動失敗"));
                    //調用discoverServices函數會觸發onServicesDiscovered函數
            //停止掃描
            mBluetoothController.stopScanLeDevice(mScanCallback);
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED || newState == BluetoothProfile.STATE_DISCONNECTING) {
            L.e("與GATT服務器斷開連接.");
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        //發現了新服務
        if (status == BluetoothGatt.GATT_SUCCESS) {
            displayGattServices(gatt);
        } else {
            L.e("onServicesDiscovered received: " + status);
        }
    }
    @Override
    // 特徵讀取操作的結果
    public void onCharacteristicRead(BluetoothGatt gatt,  BluetoothGattCharacteristic characteristic, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
        }
    }

    /**
     * 特徵值改變
     */
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);        
        byte[] data = characteristic.getValue();
    }
};

讀取 BLE 屬性

一旦您的Android應用程序連接到GATT服務器並發現了服務,它就可以在支持的位置讀取和寫入屬性。

private void displayGattServices(BluetoothGatt gatt) {
    List<BluetoothGattService> gattServices = gatt.getServices();
    if (gattServices == null) return;

    for (BluetoothGattService gattService : gattServices) {

        for (BluetoothGattCharacteristic bluetoothGattCharacteristic : gattService.getCharacteristics()) {
        
        }
    }
}

收到GATT通知

BLE應用程序通常會要求在設備上的特定特徵發生變化時收到通知。此代碼段顯示如何使用setCharacteristicNotification()方法設置特徵的通知:

mBluetoothGatt.setCharacteristicNotification(bluetoothGattCharacteristic, true);
BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptor(
        bluetoothGattCharacteristic.getUuid());
if (descriptor != null) {
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

    mBluetoothGatt.writeDescriptor(descriptor);
}

爲特性啓用通知後,如果遠程設備上的特性發生更改,則會觸發onCharacteristicChanged()回調:

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

釋放資源

當應用程序使用完藍牙設備應該將其關閉,以節約資源、電量。

@Override
protected void onDestroy() {
    if (mBluetoothGatt != null) {
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }
    super.onDestroy();
}

本篇博客的完整代碼已經上傳至GitHub:BlueToothLeClientDemo

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