计划写一篇关于Android BLE 4.0的文章很长时间,因为各种原因一直没有去整理,趁着项目告一段落,公司的事情也相对的没有那么繁忙抽时间整理好资料并且发在github上然后也整理了关于自己4年多开发BLE 4.0的项目的一些心得。
网络上现有很多的关于Ble的项目都写得大而全,多数的实现是以事件监听机制实现导致初学者很难去理解和更好的实现;而本人在项目开发中对观察者模式存在极高的爱好,所以写出来的自然就是以观察者模式实现的一套对于BLE4.0的封装。
整套框架实现分为:蓝牙扫描,蓝牙连接,数据传输 三大模块
前言,项目导入
目前框架已上传github,使用只需要
在当前主应用下引入
implementation 'com.github.karlwe:YscocoBle:v1.0.18'
在工程下的build.gradle下面引入
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
并且在合适的位置(个人习惯在主Application)配置需要Notify的BluetoothGattService UUID和对应的BluetoothGattCharacteristic UUID,因为项目中大部分时间用到的都是唯一通道进行Notify和Write,所以我设置了默认的Notify和Write通道
config.SERVICE_UUID1 对应BluetoothGattService UUID,
config.CHA_WRITE 对应写的通道
config.CHA_NOTIFY 对应notify通道
BleConfig config = new BleConfig();
config.SERVICE_UUID1 = BleConstans.SERVICE_UUID1;
config.CHA_NOTIFY = BleConstans.CHA_NOTIFY;
config.CHA_WRITE = BleConstans.CHA_WRITE;
List<NotifyUUIDBean> beansList = new ArrayList<NotifyUUIDBean>();
/*可以向beansList添加多BluetoothGattService UUID跟BluetoothGattCharacteristic UUID同时按顺序打开多个
*notify
*/
beansList.add(new NotifyUUIDBean(BleConstans.SERVICE_UUID1,BleConstans.CHA_NOTIFY));
config.setNotifyList(beansList);
config.setPROJECT_NAME("yscoco");//本地log日志名称
config.setCloseFile(false);//是否开启写入本地log日志
config.setBleLog(true,"BLE:");//控制台显示打印
BleManage.getInstance().init(this,config);
下面代码为我目前上传项目的一些配置
// service
public static final String SERVICE_UUID1 = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
//通知
public static final String CHA_NOTIFY = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
//写数据
public static final String CHA_WRITE = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
如果你有多个notify通道需要开启可以通过
beansList.add(new NotifyUUIDBean(BleConstans.SERVICE_UUID1,BleConstans.CHA_NOTIFY));
添加多个不同通知的UUID配置;
通过这几步就可以配置好BLE通讯的基本框架,然后我们就开始去写我们的BLE扫描连接;
BLE 蓝牙扫描
BLE扫描分为两个不同的版本,Android 5.0及以下两个版本,目前封装的主要是BleScannerListener接口,和一个ScannerDriver接口
BleScannerListener
/**
* 蓝牙扫描状态回调
* OPEN_SCANNER 正在扫描
* CLOSE_SCANNER 关闭扫描
*/
public void scanState(BleScannerState state);
/**
* 扫描到设备回调
*/
public void scan(BlueDevice device);
ScannerDriver
public interface ScannerDriver {
/**
* 扫描BLE设备
* @param name 扫描的设备名称,为空代表扫描全部
* @param type 过滤的方式
*/
public void scan(String name, ScanNameType type);
/**
* 停止扫描
*/
public void stop();
/**
* 添加BleScannerListener观察者
*/
public void addBleScannerLister(BleScannerListener listener);
/**
* 移除BleScannerListener观察者
*/
public void removeBleScannerLister(BleScannerListener listener);
/**
*查询当前是否正在扫描,重复开启扫描会导致蓝牙扫描不到任何设备,
* 手机重启蓝牙后才能使用
*/
public boolean isScanner();
}
public enum ScanNameType {
START,/蓝牙名称起头/
END,/蓝牙名称结尾/
ALL,/蓝牙名称/
MATCHING;/模糊查询/
}
使用方式为对希望接收到扫描结果的Activity或者类实现BleScannerListener接口并且添加监听
BleManage.getInstance().getMyBleScannerDriver().addBleScannerLister(this);
为了防止内存泄漏的,记得移除监听
BleManage.getInstance().getMyBleScannerDriver().removeBleScannerLister(this);
为了便于显示和监听界面变化,BleScannerListener接口的数据回调全部通过Handler转换成UI线程进行显示。
BLE 蓝牙连接
- 单连接
实现方法为
BleManage.getInstance().getMySingleDriver()
基础方法有
/**
* 作者:karl.wei
* 创建日期: 2017/12/09
* 邮箱:karl.wei@yscoco.com
* QQ:2736764338
* 类介绍:设备管理基类
*/
public interface SingleBleDriver {
/**
* 连接设备
* @param mac
* @return
*/
boolean connect(String mac, BluetoothDevice mDevice, boolean isReconnect);
/**
* 设备连接状态
* @return
*/
DeviceState getDeviceState();
/**
* 断开连接的设备
* @param isReconnect 是否重连
*/
void disConnect(boolean isReconnect);
BluetoothDevice getConnectDevice();
/**
* 设置设备是否重连
* @param isReconnect 是否重连
*/
void setReconnect(boolean isReconnect);
boolean isReconnect();
void addBleStateListener(BleStateListener listener);
void removeBleStateListener(BleStateListener listener);
void addBleDataListener(BleDataListener listener);
void removeBleDataListener(BleDataListener listener);
/**
* 读取RSSI值
*/
void startReadRssi();
/**
* 停止读取RSSI值
*/
void stopReadRssi();
/**
* 发送数据
*/
boolean writeData(byte[] cmd);
/**
* 发送特定服务和属性的数据
*/
boolean writeData(byte[] cmd, String serviceUUID, String charUUID);
/**
* 发送数据
* @param writeType BluetoothGattCharacteristic.WRITE_...
*/
boolean writeData(byte[] cmd,int writeType);
/**
* 发送特定服务和属性的数据
* @param writeType BluetoothGattCharacteristic.WRITE_...
*/
boolean writeData(byte[] cmd, String serviceUUID, String charUUID,int writeType);
/**
* 读取数据
*/
void readData();
/**
* 读取特定服务和属性的数据
*/
void readData(String serviceUUID, String charUUID);
/**
* 获取需要回连的设备列表
*/
Set<String> getReconnect();
/**
* 添加需要回连的设备列表
*/
void addReconnect(String mac);
/**
* 移除需要回连的设备
*/
void removeReconnect(String mac);
/**
* 判断当前设备是否在回连列表
*/
boolean isReconnectList(String mac);
/*设置应用是否处于销毁状态*/
void setFinish(boolean isFinish);
/*获取设备信息*/
BluetoothDevice getDevice(String mac);
}
- 多连接
BleManage.getInstance().getMyMoreDriver();
/**
* 连接设备
* @param mac
* @return
*/
boolean connect(String mac, BluetoothDevice mDevice, boolean isReconnect);
/**
* 设备是否连接
* @param mac
* @return
*/
DeviceState getDeviceState(String mac);
/**
* 设备是否断开连接过
* @param mac
* @return
*/
boolean isDisConnect(String mac);
/**
* 断开连接的设备
* @param mac
* @param isReconnect 是否重连,不重连则会移除该设备的重连信息
*/
void disConnect(String mac, boolean isReconnect);
/**
* 获取当前连接的设备
*/
ArrayList<BluetoothDevice> getConnectDevice();
/**
* 断开所有连接的设备
* @param mac 忽略掉的设备,为空代表不忽略
*/
void disConnectAll(String mac);
/**
* 设置设备是否重连,主动断开连接或者移除设备前需要设置
* @param mac
* @param isReconnect 是否重连
*/
void setReconnect(String mac, boolean isReconnect);
/**
* 设备是否在重连列表,扫描到设备后判断是否发起重连
* @param mac
*/
boolean isReconnect(String mac);
void addBleStateListener(BleStateListener listener);
void removeBleStateListener(BleStateListener listener);
void addBleDataListener(BleDataListener listener);
void removeBleDataListener(BleDataListener listener);
/**
* 读取RSSI值
*/
void startReadRssi(String mac);
/**
* 停止读取RSSI值
*/
void stopReadRssi(String mac);
/**
* 发送数据
*/
boolean writeData(String mac, byte[] cmd);
/**
* 发送特定服务和属性的数据
*/
boolean writeData(String mac, byte[] cmd, String serviceUUID, String charUUID);
/**
* 发送数据
* @param writeType 数据写入类型
*/
boolean writeData(String mac, byte[] cmd,int writeType);
/**
* 发送特定服务和属性的数据
* @param writeType
*/
boolean writeData(String mac, byte[] cmd, String serviceUUID, String charUUID,int writeType);
/**
* 读取数据
*/
void readData(String mac);
/**
* 读取特定服务和属性的数据
*/
void readData(String mac, String serviceUUID, String charUUID);
/**
* 获取当前连接的设备列表
*/
int getConnectSize();
/**
* 获取需要回连的设备列表
*/
Set<String> getReconnect();
/**
* 添加需要回连的设备列表
*/
void addReconnect(String mac);
/**
* 移除需要回连的设备
*/
void removeReconnect(String mac);
/**
* 判断当前设备是否在回连列表
*/
boolean isReconnectList(String mac);
连接主要实现的接口为 BleStateListener接口,
package com.yscoco.blue.listener;
import com.yscoco.blue.enums.DeviceState;
/**
* 作者:karl.wei
* 创建日期: 2017/12/09
* 邮箱:karl.wei@yscoco.com
* QQ:2736764338
* 类介绍:蓝牙连接状态回调
*/
public interface BleStateListener {
/**
* 设备状态改变
* @param mac
*/
void deviceStateChange(String mac,DeviceState state);
/**
* 发现服务
* @param mac
*/
void onNotifySuccess(String mac);
/**
* 重连机制
* @param mac
*/
void reConnected(String mac);
}
(単连接)
BleManage.getInstance().getMySingleDriver().addBleStateListener(BleStateListener listener); 设置状态监听
(多连接)
BleManage.getInstance().getMySingleDriver().addBleStateListener(BleStateListener listener); 设置状态监听
(単连接)
BleManage.getInstance().getMySingleDriver().removeBleStateListener(BleStateListener listener); 移除状态监听
(多连接)
BleManage.getInstance().getMySingleDriver().removeBleStateListener(BleStateListener listener); 移除状态监听
BleStateListener 方法:
/**
* 设备状态改变
* @param mac
*/
void deviceStateChange(String mac,DeviceState state);
Mac 为当前响应的设备mac地址
Devicestate 为设备状态,有一下几种状态
/*连接中*/
CONNECTING,
/*连接成功*/
CONNECT,
/*断开连接*/
DISCONNECT,
/*断开连接中*/
DISCONNECTING,
/*不存在*/
UNKNOW;
方法二
/**
* 发现服务
* @param mac
*/
void onNotifySuccess(String mac);
此方法用于判断设备是否将通道全部开启,可以接收到设备Notify数据,此时可以进行数据同步操作
方法三:
/**
* 重连机制
* @param mac
*/
void reConnected(String mac);
开启了设备重连的方法后,当设备断开连接时会回调reconnected此方法,可以做重连操作
设备连接
(単连接)
BleManage.getInstance().getMySingleDriver().connect(String mac, BluetoothDevice mDevice, boolean isReconnect);
mac :设备地址
mDevice:当前设备
isReconnect:是否开启重连
(多连接)
BleManage.getInstance().getMyMoreDriver().connect(String mac, BluetoothDevice mDevice, boolean isReconnect);
mac :设备地址
mDevice:当前设备
isReconnect:是否开启重连
设备数据处理
接下来就是最核心的功能,与设备进行数据通信
BLE 4.0 是通过byte[]数组进行通信,并且byte[]数组的长度不超过20个字节
Notify(设备向APP端传输数据)
通信的主要接口为:
BleDataListener 接口
/**
* 获取到设备传输过来数据
*/
void notify(String mac,String charUUID,byte[] changeByte);
void read(String mac,String charUUID,byte[] changeByte);
使用方法:
(単连接)
BleManage.getInstance().getMySingleDriver().addBleDataListener(BleStateListener listener); 设置状态监听
(多连接)
BleManage.getInstance().getMySingleDriver().addBleDataListener(BleDataListener listener); 设置状态监听
(単连接)
BleManage.getInstance().getMySingleDriver().removeBleDataListener(BleDataListener listener); 移除状态监听
(多连接)
BleManage.getInstance().getMySingleDriver().removeBleDataListener(BleDataListener listener); 移除状态监听
这样我们就集成好了数据的监听功能,接下来我们详细讲解设备的Write跟Read功能
Write(向设备写入数据)
単连接写入数据主要四个方法
/**
* 发送数据
/
boolean writeData(byte[] cmd);
/*
* 发送特定服务和属性的数据
*/
boolean writeData(byte[] cmd, String serviceUUID, String charUUID);
/**
* 发送数据
* @param writeType BluetoothGattCharacteristic.WRITE_...
*/
boolean writeData(byte[] cmd,int writeType);
/**
* 发送特定服务和属性的数据
* @param writeType BluetoothGattCharacteristic.WRITE_...
*/
boolean writeData(byte[] cmd, String serviceUUID, String charUUID,int writeType);
其实对应的是两个方法,只是因为前文我们设置了主要的通信通道所以可以省略掉;
config.SERVICE_UUID1 对应BluetoothGattService UUID,
config.CHA_WRITE 对应写的通道
writeType 类型为数据写入类型,每个硬件工程师在编写时都有习惯,writeType 对应不上时会导致数据无法写入,常用参数:
BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
BluetoothGattCharacteristic.WRITE_TYPE_SIGNED
多连接写入
方法类似于単连接,只是多了一个针对特定设备的写入
/**
* 发送数据
/
boolean writeData(String mac, byte[] cmd);
/*
* 发送特定服务和属性的数据
*/
boolean writeData(String mac, byte[] cmd, String serviceUUID, String charUUID);
/**
* 发送数据
* @param writeType 数据写入类型
*/
boolean writeData(String mac, byte[] cmd,int writeType);
/**
* 发送特定服务和属性的数据
* @param writeType
*/
boolean writeData(String mac, byte[] cmd, String serviceUUID, String charUUID,int writeType);
如果mac传null则是对所有已连接的设备写入数据:
Read(读取特定通道)
単连接
/**
* 读取数据
*/
void readData();
/**
* 读取特定服务和属性的数据
*/
void readData(String serviceUUID, String charUUID);
多连接
/**
* 读取数据
*/
void readData(String mac);
/**
* 读取特定服务和属性的数据
*/
void readData(String mac, String serviceUUID, String charUUID);
基本的通信已经结束。
有实现好的APP跟设备通信完整demo,可搭配硬件调试;