Android BLE基礎框架全新改版

幻影宇寰發表於2017-10-31

Android BLE基礎操作框架,基於回撥,操作簡單。包含掃描、多連線、廣播包解析、服務讀寫及通知等功能。

功能

  • 支援多裝置連線管理;

  • 支援廣播包解析;

  • 支援自定義掃描過濾條件;

  • 支援根據裝置名稱正規表示式過濾掃描裝置;

  • 支援根據裝置訊號最小值過濾掃描裝置;

  • 支援根據裝置名稱或 MAC 地址列表過濾掃描裝置;

  • 支援根據裝置 UUID 過濾掃描裝置;

  • 支援根據指定裝置名稱或 MAC 地址搜尋指定裝置;

  • 支援連線裝置失敗重試;

  • 支援操作裝置資料失敗重試;

  • 支援繫結資料收發通道,同一種能力可繫結多個通道;

  • 支援註冊和取消通知監聽;

  • 支援配置最大連線數,超過最大連線數時會依據 Lru 演算法自動斷開最近最久未使用裝置;

  • 支援配置掃描、連線和運算元據超時時間;

  • 支援配置連線和運算元據重試次數以及重試間隔時間。

簡介

打造該庫的目的是為了簡化藍芽裝置接入的流程。該庫是 BLE 操作的基礎框架,只處理 BLE 裝置通訊邏輯,不包含具體的資料處理,如資料的分包與組包等。該庫提供了多裝置連線管理,可配置最大連線數量,並在超過最大連線數時會依據 Lru 演算法自動斷開最近最久未使用裝置。該庫還定製了常用的掃描裝置過濾規則,也支援自定義過濾規則。該庫所有操作都採用回撥機制告知上層呼叫的結果,操作簡單,接入方便。

效果展示

BLE效果
BLE效果

使用介紹

許可權配置

6.0 以下系統不需要配置許可權,庫中已經配置瞭如下許可權:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>複製程式碼

而如果手機系統在 6.0 以上則需要配置如下許可權:

<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>複製程式碼

因為藍芽在 6.0 以上手機使用了模糊定位功能,所以需要新增模糊定位許可權。

引入 SDK

在工程 module 的 build.gradle 檔案中的 dependencies 中新增如下依賴:

compile 'com.vise.xiaoyaoyou:baseble:2.0.0'複製程式碼

構建完後就可以直接使用該庫的功能了。

初始化

在使用該庫前需要進行初始化,初始化程式碼如下所示:

//藍芽相關配置修改
ViseBle.config()
        .setScanTimeout(-1)//掃描超時時間,這裡設定為永久掃描
        .setConnectTimeout(10 * 1000)//連線超時時間
        .setOperateTimeout(5 * 1000)//設定資料操作超時時間
        .setConnectRetryCount(3)//設定連線失敗重試次數
        .setConnectRetryInterval(1000)//設定連線失敗重試間隔時間
        .setOperateRetryCount(3)//設定資料操作失敗重試次數
        .setOperateRetryInterval(1000)//設定資料操作失敗重試間隔時間
        .setMaxConnectCount(3);//設定最大連線裝置數量
//藍芽資訊初始化,全域性唯一,必須在應用初始化時呼叫
ViseBle.getInstance().init(this);複製程式碼

初始化可以是在 Application 中也可以是在 MainActivity 中,只需要是在使用藍芽功能前就行。還有需要注意的是,藍芽配置必須在藍芽初始化前進行修改,如果預設配置滿足要求也可以不修改配置。

裝置掃描

庫中針對裝置掃描定義了幾種常用過濾規則,如果不滿足要求也可以自己定義過濾規則,下面針對庫中提供的過濾規則使用方式一一介紹:

  • 掃描所有裝置

    ViseBle.getInstance().startScan(new ScanCallback(new IScanCallback() {
      @Override
      public void onDeviceFound(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanTimeout() {
    
      }
    }));複製程式碼
  • 掃描指定裝置 MAC 的裝置

    //該方式是掃到指定裝置就停止掃描
    ViseBle.getInstance().startScan(new SingleFilterScanCallback(new IScanCallback() {
      @Override
      public void onDeviceFound(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanTimeout() {
    
      }
    }).setDeviceMac(deviceMac));複製程式碼
  • 掃描指定裝置名稱的裝置

    //該方式是掃到指定裝置就停止掃描
    ViseBle.getInstance().startScan(new SingleFilterScanCallback(new IScanCallback() {
      @Override
      public void onDeviceFound(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanTimeout() {
    
      }
    }).setDeviceName(deviceName));複製程式碼
  • 掃描指定 UUID 的裝置

    ViseBle.getInstance().startScan(new UuidFilterScanCallback(new IScanCallback() {
      @Override
      public void onDeviceFound(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanTimeout() {
    
      }
    }).setUuid(uuid));複製程式碼
  • 掃描指定裝置 MAC 或名稱集合的裝置

    ViseBle.getInstance().startScan(new ListFilterScanCallback(new IScanCallback() {
      @Override
      public void onDeviceFound(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanTimeout() {
    
      }
    }).setDeviceMacList(deviceMacList).setDeviceNameList(deviceNameList));複製程式碼
  • 掃描指定訊號範圍或裝置正則名稱的裝置

    ViseBle.getInstance().startScan(new RegularFilterScanCallback(new IScanCallback() {
      @Override
      public void onDeviceFound(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
    
      }
    
      @Override
      public void onScanTimeout() {
    
      }
    }).setDeviceRssi(rssi).setRegularDeviceName(regularDeviceName));複製程式碼

其中掃描到的裝置列表由 BluetoothLeDeviceStore 管理,而單個裝置資訊都統一放到BluetoothLeDevice中,其中包含了裝置的所有資訊,如裝置名稱、裝置地址、廣播包解析資訊等,裝置的相關資訊會在裝置詳情中進行介紹。

裝置連線

裝置連線有三種方式,一種是根據裝置資訊直接進行連線,另外兩種是在沒掃描的情況下直接通過裝置名稱或裝置 MAC 進行掃描連線。三種連線方式使用如下:

  • 根據裝置資訊連線裝置

    ViseBle.getInstance().connect(bluetoothLeDevice, new IConnectCallback() {
      @Override
      public void onConnectSuccess(DeviceMirror deviceMirror) {
    
      }
    
      @Override
      public void onConnectFailure(BleException exception) {
    
      }
    
      @Override
      public void onDisconnect(boolean isActive) {
    
      }
    });複製程式碼
  • 根據裝置 MAC 直接掃描並連線

    ViseBle.getInstance().connectByMac(deviceMac, new IConnectCallback() {
      @Override
      public void onConnectSuccess(DeviceMirror deviceMirror) {
    
      }
    
      @Override
      public void onConnectFailure(BleException exception) {
    
      }
    
      @Override
      public void onDisconnect(boolean isActive) {
    
      }
    });複製程式碼
  • 根據裝置名稱直接掃描並連線

    ViseBle.getInstance().connectByName(deviceName, new IConnectCallback() {
      @Override
      public void onConnectSuccess(DeviceMirror deviceMirror) {
    
      }
    
      @Override
      public void onConnectFailure(BleException exception) {
    
      }
    
      @Override
      public void onDisconnect(boolean isActive) {
    
      }
    });複製程式碼

裝置詳情

DEVICE INFO(裝置資訊)

  • 獲取裝置名稱(Device Name):bluetoothLeDevice.getName()
  • 獲取裝置地址(Device Address):bluetoothLeDevice.getAddress()
  • 獲取裝置類別(Device Class):bluetoothLeDevice.getBluetoothDeviceClassName()
  • 獲取主要裝置類別(Major Class):bluetoothLeDevice.getBluetoothDeviceMajorClassName()
  • 獲取服務類別(Service Class):bluetoothLeDevice.getBluetoothDeviceKnownSupportedServices()
  • 獲取配對狀態(Bonding State):bluetoothLeDevice.getBluetoothDeviceBondState()

RSSI INFO(訊號資訊)

  • 獲取第一次訊號時間戳(First Timestamp):bluetoothLeDevice.getFirstTimestamp()
  • 獲取第一次訊號強度(First RSSI):bluetoothLeDevice.getFirstRssi()
  • 獲取最後一次訊號時間戳(Last Timestamp):bluetoothLeDevice.getTimestamp()
  • 獲取最後一次訊號強度(Last RSSI):bluetoothLeDevice.getRssi()
  • 獲取平均訊號強度(Running Average RSSI):bluetoothLeDevice.getRunningAverageRssi()

SCAN RECORD INFO(廣播資訊)

根據掃描到的廣播包AdRecordStore獲取某個廣播資料單元AdRecord的型別編號record.getType(),再根據編號獲取廣播資料單元的型別描述record.getHumanReadableType()以及該廣播資料單元的長度及資料內容,最後通過AdRecordUtil.getRecordDataAsString(record)將資料內容轉換成具體字串。更多關於廣播包解析可以參考Android BLE學習筆記中資料解析部分。

傳送資料

在傳送資料前需要先繫結寫入資料通道,繫結通道的同時需要設定寫入資料的回撥監聽,具體程式碼示例如下:

BluetoothGattChannel bluetoothGattChannel = new BluetoothGattChannel.Builder()
        .setBluetoothGatt(deviceMirror.getBluetoothGatt())
        .setPropertyType(PropertyType.PROPERTY_WRITE)
        .setServiceUUID(serviceUUID)
        .setCharacteristicUUID(characteristicUUID)
        .setDescriptorUUID(descriptorUUID)
        .builder();
deviceMirror.bindChannel(new IBleCallback() {
    @Override
    public void onSuccess(byte[] data, BluetoothGattChannel bluetoothGattChannel, BluetoothLeDevice bluetoothLeDevice) {

    }

    @Override
    public void onFailure(BleException exception) {

    }
}, bluetoothGattChannel);
deviceMirror.writeData(data);複製程式碼

這裡的 deviceMirror 在裝置連線成功後就可以獲取到,需要注意的是,服務一樣的情況下寫入資料的通道只需要註冊一次,如果寫入資料的通道有多個則可以繫結多個。寫入資料必須要在繫結寫入資料通道後進行,可以在不同的地方多次寫入。

接收資料

與傳送資料一樣,接收裝置傳送的資料也需要繫結接收資料通道,這裡有兩種方式,一種是可通知方式、一種是指示器方式,使用方式如下:

  • 可通知方式

    BluetoothGattChannel bluetoothGattChannel = new BluetoothGattChannel.Builder()
          .setBluetoothGatt(deviceMirror.getBluetoothGatt())
          .setPropertyType(PropertyType.PROPERTY_NOTIFY)
          .setServiceUUID(serviceUUID)
          .setCharacteristicUUID(characteristicUUID)
          .setDescriptorUUID(descriptorUUID)
          .builder();
    deviceMirror.bindChannel(new IBleCallback() {
      @Override
      public void onSuccess(byte[] data, BluetoothGattChannel bluetoothGattChannel, BluetoothLeDevice bluetoothLeDevice) {
    
      }
    
      @Override
      public void onFailure(BleException exception) {
    
      }
    }, bluetoothGattChannel);
    deviceMirror.registerNotify(false);複製程式碼
  • 指示器方式

    BluetoothGattChannel bluetoothGattChannel = new BluetoothGattChannel.Builder()
          .setBluetoothGatt(deviceMirror.getBluetoothGatt())
          .setPropertyType(PropertyType.PROPERTY_INDICATE)
          .setServiceUUID(serviceUUID)
          .setCharacteristicUUID(characteristicUUID)
          .setDescriptorUUID(descriptorUUID)
          .builder();
    deviceMirror.bindChannel(new IBleCallback() {
      @Override
      public void onSuccess(byte[] data, BluetoothGattChannel bluetoothGattChannel, BluetoothLeDevice bluetoothLeDevice) {
    
      }
    
      @Override
      public void onFailure(BleException exception) {
    
      }
    }, bluetoothGattChannel);
    deviceMirror.registerNotify(true);複製程式碼

    在繫結通道後需要註冊通知,並需要在收到註冊成功的回撥時呼叫如下程式碼設定監聽:

    deviceMirror.setNotifyListener(bluetoothGattInfo.getGattInfoKey(), new IBleCallback() {
      @Override
      public void onSuccess(byte[] data, BluetoothGattChannel bluetoothGattChannel, BluetoothLeDevice bluetoothLeDevice) {
    
      }
    
      @Override
      public void onFailure(BleException exception) {
    
      }
    });複製程式碼

    所有裝置傳送過來的資料都會通過上面的監聽得到,如果不想監聽也可以取消註冊,使用方式如下:

    deviceMirror.unregisterNotify(isIndicate);複製程式碼

    isIndicate 表示是否是指示器方式。

讀取資料

由於讀取裝置資訊基本每次的通道都不一樣,所以這裡與上面收發資料有點不一樣,每次讀取資料都需要繫結一次通道,使用示例如下:

BluetoothGattChannel bluetoothGattChannel = new BluetoothGattChannel.Builder()
        .setBluetoothGatt(deviceMirror.getBluetoothGatt())
        .setPropertyType(PropertyType.PROPERTY_READ)
        .setServiceUUID(serviceUUID)
        .setCharacteristicUUID(characteristicUUID)
        .setDescriptorUUID(descriptorUUID)
        .builder();
deviceMirror.bindChannel(new IBleCallback() {
    @Override
    public void onSuccess(byte[] data, BluetoothGattChannel bluetoothGattChannel, BluetoothLeDevice bluetoothLeDevice) {

    }

    @Override
    public void onFailure(BleException exception) {

    }
}, bluetoothGattChannel);
deviceMirror.readData();複製程式碼

總結

從以上的描述中可以知道,裝置相關的所有操作都統一交給 ViseBle 進行處理,並且該類是單例模式,全域性只有一個,管理很方便。使用該庫提供的功能前必須要呼叫 ViseBle.getInstance().init(context); 進行初始化。每連線成功一款裝置都會在裝置映象池中新增一款裝置映象,該裝置映象是維護裝置連線成功後所有操作的核心類,在斷開連線時會將該裝置映象從映象池中移除,如果連線裝置數量超過配置的最大連線數,那麼裝置映象池會依據 Lru 演算法自動移除最近最久未使用裝置並斷開連線。ViseBle 中封裝了幾個常用的 API,如:開始掃描與停止掃描、連線與斷開連線、清除資源等,該庫提供的功能儘量簡單易用,這也正是該專案的宗旨。

相關文章