搭建Android與多ble藍牙設備並發通訊小框架 --Android連接多ble藍牙設備--Android與多ble藍牙設備連接


  此框架支持多種不同類型的ble設備,同時連接、收發數據,互不干擾。比如APP同時連兩個LED藍牙燈、兩個手環、一個藍牙加熱器,當然連接單個ble設備,或者只連接一種ble設備同樣適用本框架。

前言

  小白請繞道百度,本文適合有一定Android、ble藍牙、面向對象基礎的同學進階探討,只講關鍵技術點,細節自行腦補

  看過很多藍牙demo、開源庫,沒發現真正以面向對象的思維寫的,把自己的一套框架開源出來,希望對看到的有緣人有用,特別是面向對象思維方面。不是說定義了類,就叫面向對象,希望你能領悟

  (連接不可超過7個,極少數手機不可超過5個)

  github源碼:https://github.com/ruigeyun/Android-DualBle

  轉載引用請注明出處,尊重勞動者,讓開源發揚光大! 原創--老凱瑞的博客園 https://www.cnblogs.com/littlecarry/p/11889982.html

  以面向對象之名

一、理解業務需求:ble與Android APP通訊的基本內容

  (一)藍牙連接處理基本流程

  如下圖,來自  https://www.jianshu.com/p/1c42074b1430?from=groupmessage ,感謝作者

 

 

    對上圖補充:

    0、app連接ble成功后,能獲取ble的service uuid,而這個service uuid代表不同類型的設備。(掃描到設備后,大部分設備解析其廣播數據也能獲取service uuid)

    1、APP與ble可以通訊后,APP發送認證密碼給ble,認證通過后,ble同步自身信息給APP,最終才進入正常業務交互

    2、APP與ble,斷連后,自動重連

    3、APP可主動斷開ble,之后可主動連接ble回來

    4、APP可刪除ble,之后可再掃描連接回來

    5、接收到的藍牙數據包,需要把數據緩存后拼接成完整數據包,極有可能一次收到的數據包不是完整的

    6、藍牙數據分發到對應的業務接口

  (二)Android APP與藍牙多設備連接注意的點:

    1、設備一個一個連,連接成功一個再一個,如果同時連多個,可能一個都連不上。具體原因沒有深究

    2、如果一個設備被你連過,然后一系列操作后,無法再掃描到,用其他工具APP也掃描不到,說明這個設備被你連着,沒有徹底的釋放掉!如何完全釋放ble,具體看源碼,其中部分我也是參考了網上著名的藍牙框架 fastble:https://www.jianshu.com/p/795bb0a08beb ,感謝作者

    3、對APP對ble的每一步操作間,必須加延時,否則會有意想不到的問題。具體看源碼

    4、ble被斷開后,必須延時1-2秒,再去連接他(不通過掃描直接連的情況),否則會有意想不到的問題

二、分析整個系統:

    架構,是模塊及模塊之間的交互

    (一)整個藍牙業務系統分成的模塊:APP與ble連接交互模塊、APP與ble數據交互模塊、APP對所有ble整合管理模塊、其他能動輔助模塊

    1、APP與ble連接的交互:(1)APP掃描ble,必定有一個負責掃描的類;(2)掃描連接所有的ble,需要一個類專門負責連接的類;(3)ble自身的各種狀態以及數據交互,必定就有個ble類來描述這些自身屬性;(3)ble連接成功后,密碼驗證、數據同步、掉線重連,這些ble必須自發的行為,需要一個類來描述這些藍牙設備自發業務;

    2、APP與ble數據交互:(1)一個格式完整的數據包,以及這個數據包屬於哪個ble,必須由一個數據包類描述;(2)接收到數據,對數據拼包、過濾得到一個有效包的過程,需要一個緩存類描述;(3)完整的數據包最終對外分發,需要一個數據分發類描述;

    3、APP與ble整合管理:(1)統一調配各個ble間的關系(連接、斷開、刪除,發數據等),需要一個調配服務中心類描述;(2)對整個藍牙框架的管理,掃描、連接、數據處理等整合起來,需要一個框架管理類描述。並且這個類作為此框架對外的門面,所有對外的操作,都得通過它,達到隱藏這個框架的其他復雜細節的目的

    4、其他能動輔助:各種工具、日志調試

    (二)最終提取到的對象:

    1、APP與ble建立連接:掃描器,連接器,ble藍牙屬性設備,藍牙設備自發業務(重連、認證、同步)

    2、APP與ble數據交互:接收數據拼包緩存,數據包,數據分發器

    3、APP與ble整合管理:設備間調配服務中心,框架管理類

    4、其他能動輔助:工具、日志

    這里最關鍵的一個對象設計:ble藍牙屬性設備(BLELogicDevice)每個藍牙設備提煉成一個對象,APP每連接一個設備,就開辟一個此對象。每個對象分配一個mDeviceId,這個mDeviceId非常重要,它標志每一個藍牙設備,一般通過這個mDeviceId來操作藍牙設備。每個對象都有 BluetoothGattCallback 數據交互接口,這樣每個對象跟自己對應的ble設備單獨交互,互不相干。從一大堆掃描、回調、管理中解耦出來。每個設備對象從回調方法onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)拿到數據,把數據緩存到一個自身的數據緩存區(每個設備對象都有一個緩存區對象),在緩存中拼接成數據包,數據都攜帶mDeviceId作為標志,對外分發。

    另一個關鍵對象:設備間調配服務中心(BLEServerCentral),所有設備掛在其鏈表中,其負責維護各個設備對象的狀態(連接、斷開、刪除等),控制APP與各設備數據交

三、設計小結

  1、面向對象的思維:需求分析、系統構思、細化流程、提煉對象、對象整合,最終把整個系統完整描述清楚。根據自己的設計粒度,每一個類都去描繪一個事物,負有單一的職責,這是建立一個類的最基本原則。不是隨意定義了類,然后一大堆if else邏輯,面向過程的思維解決問題。如果你的代碼中,if else if 超過三層,說明你的代碼耦合度過高了,需要拆分整合了

  2、以上只是寫了關鍵的設計思路,源碼有很多拓展的地方,有緣人可以自己閱讀,代碼其實沒多少行,慢慢仔細看一下就明白了,不懂的可以在博客留言,我盡可能答復

  3、我把這個module做成了庫,自己運行下makeClockJar,就可以導出jar包,接口如何使用參考源碼

  4、源碼藍牙接收特征配置成通知方式,其他方式自行拓展。

  5、要添加很多新的行為,其實是很容易拓展的,比如添加一個配置特征,專門配置藍牙參數的。你讀得懂源碼,很容易添加

四、庫的用法

  demo里面,有具體的栗子,仔細閱讀下,很多注釋的,應該容易理解

  1、建立自己的藍牙設備對象,藍牙對象必須繼承BLEAppDevice,自定義一個唯一的設備類型id(DEVICE_TYPE_ID,至少包含服務、發送、接收三種uuid,以及重寫三個抽象方法,把對應三種uuid分別寫進去。構造方法必須如下的方式,固定兩個參數,並且調用父類的構造方法。demo中有三種藍牙設備,手環(BraceletDevice),藍牙控制led的設備(LedDevice)、藍牙控制加熱器設的備(HeaterDevice),,添加自己的新特征,如led燈顏色,heater定時時間。

public class LedDevice extends BLEAppDevice {
    private final String TAG = "BLELedDevice";
    
    public static final Integer DEVICE_TYPE_ID = 1002;
    public static final UUID SERVICE_UUID = UUID.fromString("0000ff**-0000-1000-8000-00805f9b34fb");
    private final UUID RX_CHAR_UUID = UUID.fromString("0000ff**-0000-1000-8000-00805f9b34fb");
    private final UUID TX_CHAR_UUID = UUID.fromString("0000ff**-0000-1000-8000-00805f9b34fb");

    @Override
    public UUID getServiceUUID() {
        return SERVICE_UUID;
    }
    @Override
    public UUID getRxUUID() {
        return RX_CHAR_UUID;
    }
    @Override
    public UUID getTxUUID() {
        return TX_CHAR_UUID;
    }
    
    public String dualColor = "";
    public String hardwareVersion = "";
    public int powerState = 0;
    public String nickname = "";
    
    public LedDevice(BluetoothDevice device, DataParserAdapter adapter) {
        super(device, adapter);
        
    }
}

 

  2、建立自己藍牙設備的數據包結構對象(可選),繼承DataParserAdapter,重寫相應方法。框架內部根據你定義的結構,自動幫你把藍牙回應的數據包提煉出來(主要是處理斷包、粘包問題),最終的數據包通過onDeviceRespSpliceData(BLEPacket message)方法回調給你。當然你也可以不用架構的處理算法,自己拼包,在DataCircularBuffer 類中,pushOriginalDataToBuffer(byte[] originalData)方法,是各個藍牙設備數據推過來的入口,在這里接入自己的算法。

  如果不建立DataParserAdapter對象,則默認為null,藍牙回應的數據,通過onDevicesRespOriginalData(BLEPacket message) 方法回調給你。

  3、建立自己的藍牙管理對象,繼承BLEBaseManager,重寫必要的、可選的方法。藍牙的各種信息交換,都是通過這個類回調給你。很重要!仔細閱讀BLEServerListener接口里的方法說明,重寫自己需要的方法。

  (1)必須重寫 onGetDevicesServiceUUID()方法,把自己定義的設備類型ID和設備的service uuid,用map寫進去。框架連接上設備后,讀取設備的service uuid,根據這個map分辨出是那種類型的設備。

  (2)必須重寫BLEAppDevice onCreateDevice(BluetoothDevice bluetoothDevice, int deviceType)方法,框架識別設備類型后,回調給你,你根據設備類型,創建設備對象實例。

  (3)onAddScanDevice(BluetoothDevice bluetoothDevice)方法,框架掃描到設備,就會回調這個方法。

  (4)onAddNewDevice(BLEAppDevice device)方法,框架連接成功一個設備,各種狀態完備后,回調這個方法。

  這些方法在BLEServerListener接口都有詳細說明

public class BLEManager extends BLEBaseManager {

    private final String TAG = "BLEManager";

    private static BLEManager instance = new BLEManager();
    public static BLEManager getInstance() {
        return instance;
    }

    @Override
    public HashMap<Integer, UUID> onGetDevicesServiceUUID() {
        HashMap<Integer, UUID> map = new HashMap();
        map.put(HeaterDevice.DEVICE_TYPE_ID, HeaterDevice.SERVICE_UUID);
        map.put(LedDevice.DEVICE_TYPE_ID, LedDevice.SERVICE_UUID);

        return map;
    }

    @Override
    public void onScanOver() {
        Log.w(TAG, "onScanOve。。");
    }

    @Override
    public BLEAppDevice onCreateDevice(BluetoothDevice bluetoothDevice, int deviceType) {
        if (deviceType == HeaterDevice.DEVICE_TYPE_ID) {
            //數據包解析適配器為null,藍牙設備回應的數據在 onDevicesRespOriginalData(BLEPacket message)
            return new HeaterDevice(bluetoothDevice, null);
        }
        else if (deviceType == LedDevice.DEVICE_TYPE_ID) {
            // 設置了數據包解析適配器,數據回調在 onDeviceRespSpliceData(BLEPacket message)
            return new LedDevice(bluetoothDevice, new LedDataAdapter());
        }
        else {
            return null;
        }
    }

    @Override
    public void onAddScanDevice(BluetoothDevice bluetoothDevice){
        EventBus.getDefault().post(new AddScanDeviceEvent(bluetoothDevice));
    }

    @Override
    public void onConnectUnTypeDevice(BluetoothDevice bluetoothDevice, int type) {
        EventBus.getDefault().post(new ConnectUnTypeDeviceEvent(bluetoothDevice, type));
    }

    @Override
    public void onConnectDevice(BLEAppDevice device, int type){
        EventBus.getDefault().post(new ConnectDeviceEvent(device, type));
    }

    @Override
    public void onAddNewDevice(BLEAppDevice device){
        EventBus.getDefault().post(new AddNewDeviceEvent(device));
    }
    @Override
    public void onUpdateDeviceInfo(BLEAppDevice device) {
        EventBus.getDefault().post(new updateDeviceInfoEvent(device));
    }
    @Override
    public void onDeviceSendResult(String result){
        EventBus.getDefault().post(new BleSendResultEvent(result));
    }

    @Override
    public void onDeviceRespSpliceData(BLEPacket message) {
        LogUtil.i(TAG, "onDeviceRespSpliceDat: [" + BytesUtil.BytesToHexStringPrintf(message.bleData) + "] bleId: " + message.bleId);
//        DataManager.getInstance().DecodeRespData(message.bleData, message.bleId);
    }

    @Override
    public void onDevicesRespOriginalData(BLEPacket message) {
        LogUtil.v(TAG, "onDevicesRespOriginalDat: [" + BytesUtil.BytesToHexStringPrintf(message.bleData) + "] bleId: " + message.bleId);
    }


}

 

  建立三個對象,就可以使用此框架了,如此簡單!

  4、初始化藍牙框架,APP獲得藍牙相應權限后,調用BLEBaseManager的 initBle(..)方法初始化藍牙。見demo

 注意

  1、多設備同時工作,必定引起並發競爭問題,自己要做好同步!demo只是使用方法,沒有處理那些問題

  2、此框架藍牙接收特征配置成通知方式,其他方式自行拓展,工作太忙沒有太多時間去整理,見諒!

 

demo運行起來的效果

 

更新日志

  2020-3-12 更新

1、通過廣播解析到藍牙設備的server uuid,從而確定該設備是哪種類型,創建設備,連接設備。之前是連接成功后,才獲取service uuid,然后確定其設備類型,這樣處理讓代碼邏輯比較臃腫

2、如果你的設備廣播中沒有service uuid,抱歉,設備被忽略。大部分設備都是有的
3、每次只能連接一個設備,簡化連接流程
4、修改部分變量、注釋,增加可讀性


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM