此框架支持多種不一樣類型的ble設備,同時鏈接、收發數據,互不干擾。好比APP同時連兩個LED藍牙燈、兩個手環、一個藍牙加熱器,固然鏈接單個ble設備,或者只鏈接一種ble設備一樣適用本框架。html
前言git
小白請繞道百度,本文適合有必定Android、ble藍牙、面向對象基礎的同窗進階探討,只講關鍵技術點,細節自行腦補github
看過不少藍牙demo、開源庫,沒發現真正以面向對象的思惟寫的,把本身的一套框架開源出來,但願對看到的有緣人有用,特別是面向對象思惟方面。不是說定義了類,就叫面向對象,但願你能領悟算法
(鏈接不可超過7個,極少數手機不可超過5個)緩存
github源碼:https://github.com/ruigeyun/Android-DualBle架構
轉載引用請註明出處,尊重勞動者,讓開源發揚光大! 原創--老凱瑞的博客園 http://www.javashuo.com/article/p-wokfkrbh-eq.html併發
以面向對象之名app
1、理解業務需求:ble與Android APP通信的基本內容框架
(一)藍牙鏈接處理基本流程ide
以下圖,來自 https://www.jianshu.com/p/1c42074b1430?from=groupmessage ,感謝做者
對上圖補充:
0、app鏈接ble成功後,才能讀到ble的service uuid,而這個service uuid表明不一樣類型的設備。
一、APP與ble能夠通信後,APP發送認證密碼給ble,認證經過後,ble同步自身信息給APP,最終才進入正常業務交互
二、APP與ble,斷連後,自動重連
三、APP可主動斷開ble,以後可主動鏈接ble回來
四、APP可刪除ble,以後可再掃描鏈接回來
五、接收到的藍牙數據包,須要把數據緩存後拼接成完整數據包,極有可能一次收到的數據包不是完整的
六、藍牙數據分發到對應的業務接口
(二)Android APP與藍牙多設備鏈接注意的點:
一、設備一個一個連,鏈接成功一個再一個,若是同時連多個,可能一個都連不上。具體緣由沒有深究
二、若是一個設備被你連過,而後一系列操做後,沒法再掃描到,用其餘工具APP也掃描不到,說明這個設備被你連着,沒有完全的釋放掉!如何徹底釋放ble,具體看源碼,其中部分我也是參考了網上著名的藍牙框架 fastble:https://www.jianshu.com/p/795bb0a08beb ,感謝做者
三、對APP對ble的每一步操做間,必須加延時,不然會有意想不到的問題。具體看源碼
四、ble被斷開後,必須延時1-2秒,再去鏈接他(不經過掃描直接連的狀況),不然會有意想不到的問題
2、分析整個系統:
架構,是模塊及模塊之間的交互
(一)整個藍牙業務系統分紅的模塊:APP與ble鏈接交互模塊、APP與ble數據交互模塊、APP對全部ble整合管理模塊、其餘能動輔助模塊
一、APP與ble鏈接的交互:(1)APP掃描ble,一定有一個負責掃描的類;(2)掃描鏈接全部的ble,須要一個類專門負責鏈接的類;(3)ble自身的各類狀態以及數據交互,一定就有個ble類來描述這些自身屬性;(3)ble鏈接成功後,密碼驗證、數據同步、掉線重連,這些ble必須自發的行爲,須要一個類來描述這些藍牙設備自發業務;
二、APP與ble數據交互:(1)一個格式完整的數據包,以及這個數據包屬於哪一個ble,必須由一個數據包類描述;(2)接收到數據,對數據拼包、過濾獲得一個有效包的過程,須要一個緩存類描述;(3)完整的數據包最終對外分發,須要一個數據分發類描述;
三、APP與ble整合管理:(1)統一調配各個ble間的關係(鏈接、斷開、刪除,發數據等),須要一個調配服務中心類描述;(2)對整個藍牙框架的管理,掃描、鏈接、數據處理等整合起來,須要一個框架管理類描述。而且這個類做爲此框架對外的門面,全部對外的操做,都得經過它,達到隱藏這個框架的其餘複雜細節的目的
四、其餘能動輔助:各類工具、日誌調試
(二)最終提取到的對象:
一、APP與ble創建鏈接:掃描器,鏈接器,ble藍牙屬性設備,藍牙設備自發業務(重連、認證、同步)
二、APP與ble數據交互:接收數據拼包緩存,數據包,數據分發器
三、APP與ble整合管理:設備間調配服務中心,框架管理類
四、其餘能動輔助:工具、日誌
這裏最關鍵的一個對象設計:ble藍牙屬性設備(BLELogicDevice),每一個藍牙設備提煉成一個對象,APP每鏈接一個設備,就開闢一個此對象。每一個對象分配一個mDeviceId。每一個對象都有 BluetoothGattCallback 數據交互接口,這樣每一個對象跟本身對應的ble設備單獨交互,互不相干。從一大堆掃描、回調、管理中解耦出來。每一個設備對象從回調方法onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)拿到數據,把數據緩存到一個自身的數據緩存區(每一個設備對象都有一個緩存區對象),在緩存中拼接成數據包,數據都攜帶mDeviceId做爲標誌,對外分發。
另外一個關鍵對象:設備間調配服務中心(BLEServerCentral),全部設備掛在其鏈表中,其負責維護各個設備對象的狀態(鏈接、斷開、刪除等),控制APP與各設備數據交
3、設計小結
一、面向對象的思惟:需求分析、系統構思、細化流程、提煉對象、對象整合,最終把整個系統完整描述清楚。根據本身的設計粒度,每個類都去描繪一個事物,負有單一的職責,這是創建一個類的最基本原則。不是隨意定義了類,而後一大堆if else邏輯,面向過程的思惟解決問題。若是你的代碼中,if else if 超過三層,說明你的代碼耦合度太高了,須要拆分整合了
二、以上只是寫了關鍵的設計思路,源碼有不少拓展的地方,有緣人能夠本身閱讀,代碼其實沒多少行,慢慢仔細看一下就明白了,不懂的能夠在博客留言,我儘量答覆
三、我把這個module作成了庫,本身運行下makeClockJar,就能夠導出jar包,接口如何使用參考源碼
四、源碼藍牙接收特徵配置成通知方式,其餘方式自行拓展。
五、要添加不少新的行爲,實際上是很容易拓展的,好比添加一個配置特徵,專門配置藍牙參數的。你讀得懂源碼,很容易添加
4、庫的用法
demo裏面,有具體的栗子,仔細閱讀下,不少註釋的,應該容易理解
一、創建本身的藍牙設備對象,demo中有兩種藍牙設備,藍牙控制led的設備(LedDevice)、藍牙控制加熱器設的備(HeaterDevice),他們繼承藍牙庫的對外設備(BLEAppDevice),添加本身的新特徵,如led燈顏色,heater定時時間。藍牙對象必須包含本身的服務、發送、接收三種uuid,以及自定義一個設備類型id,重寫三個抽象方法,把uuid寫進去。構造方法必須以下的方式,固定兩個參數,而且調用父類的構造方法。
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); } }
二、創建本身藍牙設備的數據包結構對象(可選),繼承DataParserAdapter,重寫相應方法。框架內部根據你定義的結構,自動幫你把藍牙迴應的數據包提煉出來(主要是處理斷包、粘包問題),最終的數據包經過onDeviceRespSpliceData(BLEPacket message)方法回調給你。固然你也能夠不用架構的處理算法,本身拼包,在DataCircularBuffer 類中,pushOriginalDataToBuffer(byte[] originalData)方法,是各個藍牙設備數據推過來的入口,在這裏接入本身的算法。
若是不創建DataParserAdapter對象,則默認爲null,藍牙迴應的數據,經過onDevicesRespOriginalData(BLEPacket message) 方法回調給你。
三、創建本身的藍牙管理對象,繼承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); } }
創建三個對象,就可使用此框架了,如此簡單!
四、初始化藍牙框架,APP得到藍牙相應權限後,調用BLEBaseManager的 initBle(..)方法初始化藍牙。見demo
注意
一、多設備同時工做,一定引發併發競爭問題,本身要作好同步!demo只是使用方法,沒有處理那些問題
二、此框架藍牙接收特徵配置成通知方式,其餘方式自行拓展,工做太忙沒有太多時間去整理,見諒!
demo運行起來的效果