此框架支持多种不一样类型的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运行起来的效果