天天看點

【Android應用開發】Android 藍牙低功耗 (BLE) ( 第一篇 . 概述 . 藍牙低功耗文檔 翻譯)

​​​​

參考 : 

-- 官方文檔 : ​​https://developer.android.com/guide/topics/connectivity/bluetooth-le.html​​;

1. 概述

BLE 概述 : 

-- 版本支援 : Android 4.3 (API Level 18) 内置架構引入了 藍牙低功耗方案 (Bluetooth Low Energy, BLE) 支援; 

-- 角色支援 : Android 手機隻能作為 主裝置 (central role), 開發者開發的 APP 可以使用其提供的 API 接口, 用于 發現裝置, 周遊服務 (services),  讀寫服務中的特性 (characteristics). 

-- 傳統藍牙對比 : 與傳統的藍牙對比, 藍牙低功耗方案 (Bluetooth Low Energy) 是出于更低的電量消耗考慮而設計的. 這可以使 Android 應用可以與 BLE 裝置進行交流, 這些裝置需要很低的電量, 如 近距離傳感器, 心率測量裝置, 健康裝置 等等.

2. 關鍵術語 和 概念

(1) Generic Attribute Profile (GATT) 通用屬性規範

Generic Attribute Profile (GATT) 通用屬性規範 : 

-- GATT 作用 : GATT 規範是一個針對 在 BLE 連接配接上的, 發送 和 接收 少量資料的一個規範, 所有的現有的低功耗應用的規範都是基于這個 GATT 規範制定的.

-- 制定者 : 藍牙技術聯盟 (Bluetooth SIG) 為低功耗裝置定義了許多規範, 一個 規範 (Profile) 就是 裝置如何在特定的應用中工作的詳述. 

-- 裝置規範對應關系 : 此外, 一個裝置可以實作多個規範, 如 : 一個裝置可以包含一個心率檢測器, 和 電量檢測器.

(2) Attribute Protocol (ATT) 屬性協定

Attribute Protocol (ATT) 屬性協定 : 

-- ATT 與 GATT 關系 : GATT 規範是建立在 ATT 的上一層的, 這套改改通常被稱為 GATT/ATT. 

-- ATT 作用 : ATT 被用于優化 BLE 裝置的運作, 為了這個目的, ATT (屬性協定) 使用盡可能少的位元組. 

-- ATT 唯一辨別 : ATT 中的每個屬性都被 一個 UUID (Universally Unique Identifier) 獨一無二的進行辨別, UUID 是一個 128 比特的标準的字元串 ID, 用于資訊的唯一辨別. 

-- ATT 屬性 : ATT 中定義的屬性就是 Charicteristics (特性) 和 Services (服務);

(3) Characteristic 特性

Characteristic 特性 : 

-- Characteristic 概念 : 一個 Characteristic 特性包含了一個值 和 多個 Descriptor (描述符) 用于描述這個特性的值. 

-- 本質 : 一個特性可以被認為是一個類型, 類似于一個類.

(4) Descriptor 描述符

Descriptor 描述符 : 

-- 作用 : 描述符 被定義為一些屬性, 這些屬性用于描述 Characteristic (特性) 的值. 

-- 示例 : 例如, 一個 描述符 可以說明一個 可讀的描述, 一個 特性值的可接受範圍, 或者 一個特性值的測量單元.

(5) Service 服務

Service 服務 : 

-- 服務本質 : 服務是 Characteristic (特性) 的集合. 

-- 示例 : 如, 你可以有一個 名稱為 "Heart Rate Monitor (心率監控)" 的服務, 包含了特性 "Heart Rate Measurement (心率測量)". 

-- 參考資料 : 你可以在 bluetooth.org 官網查詢到一個基于 GATT 服務 和 規範的清單.

3. 角色 和 職責

(1) 四種角色

Android 裝置 與 BLE 裝置互動時, 裝置的角色 和 職責 : 

-- 中心裝置 和 外圍裝置 : 這個角色體系适用于 BLE 連接配接. 中心裝置角色 可以掃描, 查找廣播. 外圍裝置角色 發送廣播.

-- GATT 伺服器 和 GATT 用戶端 : 這個決定了兩個裝置之間, 一旦建議連接配接後, 如何進行互相通信.

(2) 中心裝置 和 外圍裝置

BLE 連接配接需要兩種裝置都存在 : 為了了解其中的差別, 想象一下 你有一個 Android 裝置 和 一個激活的 智能腕表 藍牙裝置. 手機支援作為 中心裝置 角色, 智能腕表 藍牙裝置支援作為外圍裝置角色, 為了建立 BLE 連接配接, 隻有外圍裝置 或者 隻有 中心裝置 都不能建立 BLE 連接配接.

(3) GATT 伺服器 和 GATT 用戶端

GATT 伺服器 和 GATT 用戶端 簡介 : 

-- GATT 伺服器 和 GATT 用戶端 角色不是固定的 : 一旦手機 和 智能腕表 裝置建立了 BLE 連接配接, 它們開始互相交換 GATT 中繼資料. 根據它們之間傳輸的資料類型, 其中的一個會扮演 GATT 伺服器的角色. 

-- 角色改變示例 : 如果 智能腕表 裝置想要向手機報告傳感器資料, 那麼智能腕表必須當做 GATT 伺服器. 如果智能腕表 想要從手機上接受更新資料, 那麼 Android 手機就是 GATT 伺服器.

-- 手機 和 裝置 都可以作為 GATT 伺服器 和 用戶端 : 在本文檔中使用的示例代碼, 在 Android 裝置上運作的 Android APP 就是 GATT 用戶端, BLE 外圍裝置 就是 GATT 伺服器. Android APP 從 GATT 伺服器上擷取資料, 伺服器的 BLE "heart rate monitor (心率監測)" 支援 "Heart Rate Profile (心率規範 - 一種 BLE 藍牙标準規範)". Android APP 也可以作為 GATT 伺服器;

4. BLE 權限

(1) 藍牙權限簡介

Android 藍牙權限簡介 : 

-- 權限作用 : 為了在應用中使用藍牙功能, 必須在 AndroidManifest.xml 中 聲明藍牙權限. 所有的藍牙通信操作都需要 藍牙權限 來允許執行, 例如 搜尋藍牙, 藍牙連接配接, 資料互動等操作.

-- 搜尋設定藍牙權限 : 如果 APP 要發起裝置搜尋 或者 管理 藍牙設定, 需要 提前聲明 BLUETOOTH_ADMIN 權限. 

-- 注意 : 使用 BLUETOOTH_ADMIN 權限的前提是 必須聲明 BLUETOOTH 權限.

(2) 藍牙權限簡介

藍牙權限示例 : 

-- AndroidManifest.xml 聲明藍牙權限示例 : 

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

-- 充當 BLE 裝置權限 : 如果你的 APP 隻需要勝任 BLE 裝置的工作, 隻需要如下配置 : 

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

(3) 動态控制 BLE 功能是否使用

動态控制 BLE 是否可用 : 不管怎樣, 如果你想要讓你的 APP 可以當做 BLE 裝置, 但是手機不支援這個操作, 你仍然可以進行如下配置, 隻是将其中的 android: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();
}      

5. 建立 BLE

(1) 建立 BLE 簡介

建立 BLE 簡介 : 

-- 驗證 BLE 功能 : 在應用可以通過 BLE 互動之前, 你需要驗證裝置是否支援 BLE 功能, 如果支援, 确定它是可以使用的. 

-- 注意 : 這個檢查隻有在 下面的配置 設定為 false 時才是必須的;

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

-- 不支援 BLE 關閉相關功能 : 如果 Android 手機不支援 BLE 功能, 你應該優雅的 關閉 BLE 相關功能. 

-- 支援 BLE 打開藍牙 : 如果 BLE 支援 BLE 功能, 但是裝置的藍牙是關閉的, 你可以在應用中請求打開裝置的藍牙子產品. 

-- 步驟總結 : 建立 BLE 藍牙的過程分成兩個步驟, 1. 擷取 BluetoothAdapter, 2. 打開 裝置的藍牙子產品.

(2) 擷取 BluetoothAdapter (藍牙擴充卡)

擷取 BluetoothAdapter 藍牙擴充卡 : 

-- BluetoothAdapter 類作用 : 所有的藍牙活動都需要 BluetoothAdapter, BluetoothAdapter 代表了裝置本身的藍牙擴充卡 (藍牙無線裝置). 整個系統中隻有一個 藍牙擴充卡, 應用可以使用 BluetoothAdapter 對象與 藍牙擴充卡硬體進行互動. 

-- 擷取 BluetoothAdapter 代碼示例 : 

// 初始化藍牙擴充卡
final BluetoothManager bluetoothManager =
        (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();      

-- 注意 : 這個方法使用了 getSystemService() 方法, 傳回了一個 BluetoothManager 執行個體對象, 從 BluetoothManager 執行個體對象中可以擷取 BluetoothAdapter 對象;

(3) 打開藍牙功能

打開藍牙 : 

-- 檢查是否可用 : 為了保證 藍牙功能是打開的, 調用 BluetoothAdapter 的 isEnable() 方法, 檢查藍牙在目前是否可用. 如果傳回 false, 說明目前藍牙不可用. 

-- 示例代碼 : 

private BluetoothAdapter mBluetoothAdapter;
...
// 确認目前裝置的藍牙是否可用, 
// 如果不可用, 彈出一個對話框, 請求打開裝置的藍牙子產品
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}      

6. 查找 BLE 裝置

(1) 查找所有的 BLE 裝置

查找 BLE 裝置 : 

-- 查找方法參數 : 為了搜尋到 BLE 裝置, 調用 BluetoothAdapter 的 startLeScan() 方法, 該方法需要一個 BluetoothAdapter.LeScanCallback 類型的參數. 你必須實作這個 LeScanCallback 接口, 因為 BLE 藍牙裝置掃描結果在這個接口中傳回. 

-- 查找政策 : 藍牙搜尋是非常耗電的, 你需要遵守以下的 中斷政策 和 不循環政策.

-- 中斷政策 : 隻要一發現藍牙裝置, 馬上中斷掃描.

-- 不循環政策 : 不要循環掃描, 設定一個掃描的最大時間限制. 一個裝置在之前可用, 繼續掃描可能會使裝置不可用, 此外繼續掃描會持續浪費電池電量.

-- 源碼示例 : 

/**
 * 搜尋 和 展示 可用的藍牙裝置 的 Activity 界面
 */
public class DeviceScanActivity extends ListActivity {

    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    // 10 秒後停止搜尋
    private static final long SCAN_PERIOD = 10000;
    ...
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // 在一個預先定義的時間段後停止掃描.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    //開始掃描
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
        ...
    }
...
}      

(2) 查找特定 BLE 裝置

查找特定 BLE 裝置 : 

-- 方法調用 : 查找特定類型的外圍裝置, 可以調用下面的方法, 這個方法需要提供一個 UUID 對象數組, 這個 UUID 數組是 APP 支援的 GATT 服務的特殊辨別.

-- 示例 : 

startLeScan(UUID[], BluetoothAdapter.LeScanCallback)      

(3) BluetoothAdapter.LeScanCallback 回調接口

掃描回調接口 : 

-- 接口作用 : BluetoothAdapter.LeScanCallback 實作類, 在這個實作類的接口中傳回 BLE 裝置掃描結果;

-- 源碼示例 : 

private LeDeviceListAdapter mLeDeviceListAdapter;
...
// 裝置掃描回調接口
private BluetoothAdapter.LeScanCallback mLeScanCallback =
        new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
            byte[] scanRecord) {
        runOnUiThread(new Runnable() {
           @Override
           public void run() {
               mLeDeviceListAdapter.addDevice(device);
               mLeDeviceListAdapter.notifyDataSetChanged();
           }
       });
   }
};      

(4) 裝置掃描類型

裝置掃描類型 : 藍牙裝置掃描 在同一個時間掃描時, 隻能掃描 BLE 裝置 或者 SPP 裝置中的一種, 不能同時掃描兩種裝置.

7. 連接配接到 GATT 服務

(1) 連接配接指定 BluetoothDevice 藍牙裝置

連接配接指定裝置 : 

-- 連接配接到 GATT 服務 : 與 BLE 裝置互動的第一步是 連接配接到 BLE 裝置中的 GATT 服務. 

-- 實作方法 : 調用 BluetoothDevice 的 connectGatt() 方法可以連接配接到 BLE 裝置的 GATT 服務. 

-- 參數解析 : connectGatt() 方法需要三個參數, 參數一 Context 上下文對象, 參數二 boolean autoConnect 是否自動連接配接掃描到的藍牙裝置, 參數三 BluetoothGattCallback 接口實作類. 

-- 用法示例 : 

mBluetoothGatt = device.connectGatt(this, false, mGattCallback);      

-- 擷取 BluetoothGatt 對象 : 調用 connectGatt() 方法可以連接配接到 BLE 裝置上的 GATT 服務, 傳回一個 BluetoothGatt 執行個體對象, 你可以使用這個對象去 管理 GATT 用戶端操作. 

-- GATT 用戶端操作 : Android APP 可以調用 GATT Client (用戶端). BluetoothGattCallback 可以用于傳遞結果到 GATT 用戶端, 如 連接配接狀态 和 更進一步的 GATT Client 操作.

(2) GATT 資料互動示例

BLE 藍牙資料互動 : 

-- 界面 : 在下面的示例中, BLE 應用提供了一個 Activity 界面, 該 Activity 界面用于 連接配接, 展示資料, 展示 GATT 服務 和 裝置支援的特性. 

-- BLE 藍牙服務類 : 基于使用者的輸入, 這個 Activity 界面可以與一個 BluetoothLeService 的服務進行交流, 該交流的本質就是 BLE 裝置的 GATT 服務 與 Android 的 BLE API 進行交流.

-- BLE 藍牙服務類 示例代碼 : 

// BLE 裝置可以通過該服務 與 Android 的 BLE API 進行互動
public class BluetoothLeService extends Service {
    private final static String TAG = BluetoothLeService.class.getSimpleName();

    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private String mBluetoothDeviceAddress;
    private BluetoothGatt mBluetoothGatt;
    private int mConnectionState = STATE_DISCONNECTED;

    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;

    public final static String ACTION_GATT_CONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED =
            "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_DATA_AVAILABLE =
            "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
    public final static String EXTRA_DATA =
            "com.example.bluetooth.le.EXTRA_DATA";

    public final static UUID UUID_HEART_RATE_MEASUREMENT =
            UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

    // BLE API 中定義的不同的回調方法.
    private final BluetoothGattCallback mGattCallback =
            new BluetoothGattCallback() {
        @Override
        // BLE 裝置的狀态改變 連接配接 斷開
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                Log.i(TAG, "連接配接到了 GATT 服務.");
                Log.i(TAG, "嘗試搜尋服務:" +
                        mBluetoothGatt.discoverServices());

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
                Log.i(TAG, "于 GATT 服務斷開連接配接.");
                broadcastUpdate(intentAction);
            }
        }

        @Override
        // BLE 裝置中 新的 GATT 服務被發現
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                Log.w(TAG, "發現 GATT 服務 : " + status);
            }
        }

        @Override
        // 特性讀取操作傳回的資料
        public void onCharacteristicRead(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic,
                int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }
     ...
    };
...
}      

-- 廣播發送 : 當一個特定的回調被觸發, 它調用适當的 broadcastUpdate() 幫助方法, 将其當做一個 Action 操作傳遞出去. 

-- 注意藍牙心率 : 這部分的資料解析 與 藍牙心率測量 是一起被執行的.

-- 廣播發送 示例代碼 : 

private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

private void broadcastUpdate(final String action,
                             final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);

    // This is special handling for the Heart Rate Measurement profile. Data
    // parsing is carried out as per profile specifications.
    // 心率監測規範的特殊處理
    // 資料解析在每個規範中完成
    if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
        int flag = characteristic.getProperties();
        int format = -1;
        if ((flag & 0x01) != 0) {
            format = BluetoothGattCharacteristic.FORMAT_UINT16;
            Log.d(TAG, "心率格式 UINT16.");
        } else {
            format = BluetoothGattCharacteristic.FORMAT_UINT8;
            Log.d(TAG, "心率格式 UINT8.");
        }
        final int heartRate = characteristic.getIntValue(format, 1);
        Log.d(TAG, String.format("接收到心跳檢測 : %d", heartRate));
        intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
    } else {
        // 對于其它的規範, 寫出 HEX 十六進制格式的資料
        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            for(byte byteChar : data)
                stringBuilder.append(String.format("%02X ", byteChar));
            intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
                    stringBuilder.toString());
        }
    }
    sendBroadcast(intent);
}      

--

處理廣播事件 : 在 DeviceControlActivity 中處理廣播事件, 示例代碼 : 

// 處理 Service 發起的的不同僚件
// ACTION_GATT_CONNECTED: 連接配接到 GATT 服務.
// ACTION_GATT_DISCONNECTED: 與 GATT 服務斷開.
// ACTION_GATT_SERVICES_DISCOVERED: 發現 GATT 服務.
// ACTION_DATA_AVAILABLE: 從 BLE 裝置中接收資料, 資料可以是 read 或者 notification 操作的結果.
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
            mConnected = true;
            updateConnectionState(R.string.connected);
            invalidateOptionsMenu();
        } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
            mConnected = false;
            updateConnectionState(R.string.disconnected);
            invalidateOptionsMenu();
            clearUI();
        } else if (BluetoothLeService.
                ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
            // 在使用者界面 顯示所有支援的服務 和 特性. 
            displayGattServices(mBluetoothLeService.getSupportedGattServices());
        } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
            displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
        }
    }
};      

8. 讀取 BLE 屬性

讀寫屬性簡介 : 

-- 讀寫屬性前提 : Android 應用連接配接到了 裝置中的 GATT 服務, 并且發現了 各種服務 (特性集合), 可以讀寫其中的屬性. 

-- 讀寫屬性代碼示例 : 周遊服務 (特性集合) 和 特性, 将其展示在 UI 界面中.

public class DeviceControlActivity extends Activity {
    ...
    // 示範如何通過其所支援的 GATT 周遊 服務 (Services) 和 特性 (Characteristics)
    // 在這個示例中, 我們将查詢出的資料填充到 UI 界面中的 ExpandableListView 中
    private void displayGattServices(List<BluetoothGattService> gattServices) {
        if (gattServices == null) return;
        String uuid = null;
        String unknownServiceString = getResources().
                getString(R.string.unknown_service);
        String unknownCharaString = getResources().
                getString(R.string.unknown_characteristic);
        ArrayList<HashMap<String, String>> gattServiceData =
                new ArrayList<HashMap<String, String>>();
        ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
                = new ArrayList<ArrayList<HashMap<String, String>>>();
        mGattCharacteristics =
                new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

        // 周遊 GATT 服務
        for (BluetoothGattService gattService : gattServices) {
            HashMap<String, String> currentServiceData =
                    new HashMap<String, String>();
            uuid = gattService.getUuid().toString();
            currentServiceData.put(
                    LIST_NAME, SampleGattAttributes.
                            lookup(uuid, unknownServiceString));
            currentServiceData.put(LIST_UUID, uuid);
            gattServiceData.add(currentServiceData);

            ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
                    new ArrayList<HashMap<String, String>>();
                    
            // 擷取服務中的特性集合
            List<BluetoothGattCharacteristic> gattCharacteristics =
                    gattService.getCharacteristics();
            ArrayList<BluetoothGattCharacteristic> charas =
                    new ArrayList<BluetoothGattCharacteristic>();
                    
           // 循環周遊特性集合
            for (BluetoothGattCharacteristic gattCharacteristic :
                    gattCharacteristics) {
                charas.add(gattCharacteristic);
                HashMap<String, String> currentCharaData =
                        new HashMap<String, String>();
                uuid = gattCharacteristic.getUuid().toString();
                currentCharaData.put(
                        LIST_NAME, SampleGattAttributes.lookup(uuid,
                                unknownCharaString));
                currentCharaData.put(LIST_UUID, uuid);
                gattCharacteristicGroupData.add(currentCharaData);
            }
            mGattCharacteristics.add(charas);
            gattCharacteristicData.add(gattCharacteristicGroupData);
         }
    ...
    }
...
}      

9. 接收 GATT 通知

GATT 通知簡介 : 

--

特性改變通知 : 當 BLE 裝置中的一些特殊的特性改變, 需要通知與之連接配接的 Android BLE 應用.

-- 代碼示例 : 使用 setCharacteristicNotification() 方法為特性設定通知.

private BluetoothGatt mBluetoothGatt;
BluetoothGattCharacteristic characteristic;
boolean enabled;
...
// 設定是否監聽某個特性改變
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
...
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
        UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);      

--

特性改變回調 : 一但特性開啟了改變通知監聽, 如果特性發生了改變, 就會回調 BluetoothGattCallback 接口中的 onCharacteristicChanged() 方法.

@Override
// 特性通知
public void onCharacteristicChanged(BluetoothGatt gatt,
        BluetoothGattCharacteristic characteristic) {
    broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}      

10. 關閉 APP 中的 BLE 連接配接

關閉 BLE 裝置連接配接 : 

-- 關閉方法 : 一旦結束了 BLE 裝置的使用, 調用 BluetoothGatt 的 close() 方法, 關閉 BLE 連接配接, 釋放相關的資源.

-- 關閉示例 : 

public void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
}      

繼續閱讀