Android藍牙那點事——深刻了解Android藍牙Bluetooth《進階篇》

深刻了解Android藍牙Bluetooth——《基礎篇》一篇中咱們對藍牙的各個版本的有了一個認識,藍牙版本的歷程及其優劣式介紹。那麼接下來我們就深刻一點繼續開車進入BLE的進及篇章。

BLE藍牙設備鏈接讀取的順序:

Markdown
Markdown

藍牙BLE4.x

BLE分爲三部分:

  • Service
  • Characteristic
  • Descriptorandroid

    這三部分都用UUID做爲惟一標識符。UUID爲這種格式:0000ffe1-0000-1000-8000-00805f9b34fb。好比有3個Service,那麼就有三個不一樣的UUID與Service對應。這些UUID都寫在硬件裏,咱們經過BLE提供的API能夠讀取到。git

  • 一個BLE終端能夠包含多個Service, 一個Service能夠包含多個Characteristic,一個Characteristic包含一個value和多個Descriptor,一個Descriptor包含一個Value。Characteristic是比較重要的,是手機與BLE終端交換數據的關鍵,讀取設置數據等操做都是操做Characteristic的相關屬性。

API相關介紹

  • 1.先介紹一下關於藍牙4.0中的一些名詞吧:
    (1)GATT(Gneric Attibute Profile)程序員

    經過ble鏈接,讀寫屬性類小數據Profile通用的規範。如今全部的ble應用Profile 都是基於GATTgithub

    • (2)ATT(Attribute Protocal) GATT是基於ATT Potocal的ATT針對BLE設備專門作的具體就是傳輸過程當中使用盡可能少的數據,每一個屬性都有個惟一的UUID,屬性chartcteristics and Service的形式傳輸。api

      • (3)Service是Characteristic的集合。
      • (4).Characteristic 特徵類型。

      好比。有個藍牙ble的血壓計。他可能包括多個Servvice,每一個Service有包括多個Characteristic數組

      注意:藍牙ble只能支持Android 4.3以上的系統 SDK>=18bash

  • 2.如下是開發的步驟:微信

  • 2.1首先獲取BluetoothManagerasync

  • 2.2獲取BluetoothAdapter
  • 2.3建立BluetoothAdapter.LeScanCallback
  • 2.4.開始搜索設備。
  • 2.5.BluetoothDevice 描述了一個藍牙設備 提供了getAddress()設備Mac地址,getName()設備的名稱。
  • 2.6開始鏈接設備
  • 2.7鏈接到設備以後獲取設備的服務(Service)和服務對應的Characteristic。
  • 2.8獲取到特徵以後,找到服務中能夠向下位機寫指令的特徵,向該特徵寫入指令。
  • 2.9寫入成功以後,開始讀取設備返回來的數據。
  • 2.十、斷開鏈接
  • 2.十一、數據的轉換方法
    大概總體就是如上的步驟。可是也是要具體根據廠家的協議來實現通訊的過程。
    那麼具體要怎麼使用呢?咱們據需開車往下走。
    一.添加權限
      和經典藍牙同樣,應用使用藍牙,須要聲明BLUETOOTH權限,若是須要掃描設備或者操做藍牙設置,則還須要BLUETOOTH_ADMIN權限:
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>複製代碼
    除了藍牙權限外,若是須要BLE feature則還須要聲明uses-feature:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>複製代碼

按時required爲true時,則應用只能在支持BLE的Android設備上安裝運行;required爲false時,Android設備都可正常安裝運行,須要在代碼運行時判斷設備是否支持BLE feature:ide

// 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();
}複製代碼

第一步:開啓藍牙:

  • 1.首先獲取有BluetoothAdapter兩種方式:
private BluetoothManager bluetoothManager;

bluetoothManager =   (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();複製代碼

或者是:

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();複製代碼

兩行方式都是能夠的。

注:這裏經過getSystemService獲取BluetoothManager,再經過BluetoothManager獲取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API level 18)。

  • 2.判斷手機設備是否有藍牙模塊
// 檢查設備上是否支持藍牙
        if (mBluetoothAdapter == null) {
            showToast("沒有發現藍牙模塊");
            return;
        }複製代碼
  • 3.開啓藍牙設備

    開啓藍牙設備有兩種方式:

    • 第一種直接簡單暴力不給用戶進行提示:

      if (!mBluetoothAdapter.isEnabled()) {
                mBluetoothAdapter.enable();
      }複製代碼
    • 第二種直優雅的踐行開啓而且有彈框進行提示,隱式啓動Intent:

      if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
      }複製代碼
  • 4.掃描藍牙設備

    我這裏是把掃描到的BLE地址存放到List集合中去。這裏咱們能夠寫一個方法進行封裝一下。

/***********
     * 掃描設備
     ********/
    private void scanLeDevice(final boolean enable) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            if (enable) {
                devices.clear();//清空集合
                // Stops scanning after a pre-defined scan period.
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                            mBluetoothAdapter.stopLeScan(mLeScanCallback);
                        }
                    }
                }, INTERVAL_TIME);
                mBluetoothAdapter.startLeScan(mLeScanCallback);
            } else {
                try {
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                } catch (Exception e) {
                }
            }
        }
    }複製代碼

在這個掃描方法中,咱們在AndroidStudio或者是Eclipse中會看到startLeScan方法會有橫線,代表這個方式顯示過時的方法,那麼

若是你只須要搜索指定UUID的外設,你能夠調用 startLeScan(UUID[], BluetoothAdapter.LeScanCallback)方法。
其中UUID數組指定你的應用程序所支持的GATT Services的UUID。

那麼LeScanCallback的初始化代碼以下:

private void initCallBack(){
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (device != null) {
                                if (!TextUtils.isEmpty(device.getName())) {
                                   // devices.add(device);
                                    String name = device.getName();
                                    if (name.contains(BluetoothDeviceAttr.OYGEN_DEVICE_NAME)) {
                                        if (!devices.contains(device)) {
                                            devices.add(device);
                                        }
                                    }
                                }
                            }
                        }
                    });
                }
            };
        } else {
            getToast("設備藍牙版本太低");
            return;
        }
}複製代碼

那麼若是在設備多的狀況下咱們講搜出不少的設備。咱們能夠選擇咱們所須要的地址進行連接。可是這類要注意的是:搜索時,你只能搜索傳統藍牙設備或者BLE設備,二者徹底獨立,不可同時被搜索.

  • 5.進行連接設備
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.w(TAG, "Device not found. Unable to connect.");
            return false;
        }
        // We want to directly connect to the device, so we are setting the autoConnect
        // parameter to false.
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);複製代碼

這裏調用的是device的connectGatt方法

/**
     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
     * The callback is used to deliver results to Caller, such as connection status as well
     * as any further GATT client operations.
     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
     * GATT client operations.
     * @param callback GATT callback handler that will receive asynchronous callbacks.
     * @param autoConnect Whether to directly connect to the remote device (false)
     *                    or to automatically connect as soon as the remote
     *                    device becomes available (true).
     * @throws IllegalArgumentException if callback is null
     */
    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
                                     BluetoothGattCallback callback) {
        return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO));
    }複製代碼

 api中闡述的是第一個參數是上下文對象Context,第二個參數是是否自動鏈接,第三個是藍牙的GattCallback回調。

private BluetoothGattCallback GattCallback = new BluetoothGattCallback() {
    // 這裏有9個要實現的方法,看狀況要實現那些,用到那些就實現那些
    //當鏈接狀態發生改變的時候
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState){

    };
    //回調響應特徵寫操做的結果。
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){

    };
    //回調響應特徵讀操做的結果。
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    }
    //當服務被發現的時候回調的結果
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    }
    當鏈接能被被讀的操做
    @Override
   public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {

            super.onDescriptorRead(gatt, descriptor, status);
      }  
    ......
};複製代碼

 鏈接的過程咱們一個經過service來進行鏈接,也能夠在activity中進行操做。
好,到此爲止,一個BLE藍牙鏈接設備的整個流程咱們已經清楚完畢。

Android4.x的藍牙不太成熟性

 可是在實際操做過程當中不免會出現一些比較坑人的問題。好比我用一個地址進行藍牙設備鏈接,偶爾會出現藍牙鏈接不上或者是說鏈接上設備後不返回數據等問題。那麼此時咱們可能會重啓一下藍牙或手機就立馬有成功了。此時咱們千萬不能矇蔽,也不要懷疑本身的人生。這是由於Android4.x的藍牙仍是不太成熟。目前能夠來講是個測試階段。

  • 手機可能會搜索不到藍牙設備
  • 有時候會在廣播中接收不到數據
  • 出現異常須要從新啓動手機或者是重啓才能恢復正常

這個時候咱們怎麼辦呢?

此時不要抱怨什麼,難到咱們做爲Android程序員就註定如此的苦逼嗎?

答案是否認的。

如何去優化呢?那麼咱們就應該從UI界面,用戶體驗上進行操做來實現

  • 作一個定時器,若是在在肯定藍牙設備一打開而且存在的狀況系,能夠在手機搜索5s內沒有搜索到藍牙設備時重啓藍牙,而且在廣播中接收到藍牙開啓後再次搜索
  • 能夠在UI上進行對用戶進行相對應的提示

    • 當藍牙爲啓動時,提示用戶去開啓器藍牙
    • 當藍牙開啓後,在處在開啓狀態後,提示用戶藍牙正在開啓...
    • 藍牙已開啓,設備並無鏈接上,提示用戶去進行鏈接
    • 設備正在鏈接上手機,提示用戶,正在鏈接,請等待...
    • 藍牙設備鏈接上手機,正在讀取,提示數據正在讀取中...

      咱們不能子在Android系統上來操做什麼,咱們在體驗上作到了咱們能作的就能夠的。

      手機藍牙鏈接BLE設備要求

  • 手機Android 4.3以上的系統 SDK>=18
  • 藍牙版本>=4.0

學習參考道demo下載地址:
github.com/androidstar…

學到這裏,關於AndroidBLE藍牙鏈接咱們已經基本上實現了藍牙的搜索,鏈接,讀取等。

你們項目中若是常常涉及到硬件好比手環,溫度計,汗液儀,心電圖,血壓計等這些ble的藍牙設備,就必定會用到藍相關方面的知識。這裏筆者先給你們提早踩一下坑,進行了總結,爲後面的小夥伴在研究藍牙方面儘可能的少踩一些坑。如多對藍牙的歷程還未有清楚的認識,請參考深刻了解Android藍牙Bluetooth4.0——《基礎篇》

若是你以爲此文對您有所幫助,歡迎入羣 QQ交流羣 :232203809   
微信公衆號:終端研發部

技術+職場
技術+職場

(歡迎關注學習和交流)複製代碼
相關文章
相關標籤/搜索