这是哥们目前搜到最给力的!绝对干货!
Throws
Android ble开发和IOS ble开发基本差不多,有点小区别。IOS 表现是特别稳定特别好用,重连飞快,需要操心的少。 Android目前坑还是比较多,各种错误,蓝牙奔溃等等。但,细心调理,Android也还是可以用的。
本篇文章主讲 Android, IOS的简单点,可以下次讲。
先来介绍几个类 :BluetoothGatt,BluetoothGattService,BluetoothGattCharacteristic,BluetoothGattCallback
官方介绍网址是 https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html.有兴趣的童鞋可以翻墙去看。以下是我尽可能形象的通 俗表达
BluetoothGatt
BluetoothGatt 是开发里最重要最常用到的东西了,我把它理解为 手机与 设备通信的管道。有了这个管道,收发数据就容易了。
BluetoothGatt 是通过蓝牙设备连接获得: bluetoothDevice.connectGatt(this.context, false, gattCallback);
先来谈一下这个方法:
BluetoothGatt android.bluetooth.BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)
public BluetoothGatt connectGatt (Context context, boolean autoConnect, BluetoothGattCallback callback) Added in API level 18
Connect to GATT Server hosted by this device. Caller acts as GATT client. The callback is used to deliver results to Caller,
such as connection status as well as any further GATT client operations. The method returns a BluetoothGatt instance.
You can use BluetoothGatt to conduct GATT client operations.
Parameters
本篇文章主讲 Android, IOS的简单点,可以下次讲。
先来介绍几个类 :BluetoothGatt,BluetoothGattService,BluetoothGattCharacteristic,BluetoothGattCallback
官方介绍网址是 https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html.有兴趣的童鞋可以翻墙去看。以下是我尽可能形象的通 俗表达
BluetoothGatt
BluetoothGatt 是开发里最重要最常用到的东西了,我把它理解为 手机与 设备通信的管道。有了这个管道,收发数据就容易了。
BluetoothGatt 是通过蓝牙设备连接获得: bluetoothDevice.connectGatt(this.context, false, gattCallback);
先来谈一下这个方法:
BluetoothGatt android.bluetooth.BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)
public BluetoothGatt connectGatt (Context context, boolean autoConnect, BluetoothGattCallback callback) Added in API level 18
Connect to GATT Server hosted by this device. Caller acts as GATT client. The callback is used to deliver results to Caller,
such as connection status as well as any further GATT client operations. The method returns a BluetoothGatt instance.
You can use BluetoothGatt to conduct GATT client operations.
Parameters
autoConnect | Whether to directly connect to the remote device (false) or to automatically connect as soon as the remote device becomes available (true). |
callback | GATT callback handler that will receive asynchronous callbacks. |
Throws
IllegalArgumentException | if callback is null |
--------------------------------------------------------------------------------------
autoConnect 为false 立刻发起一次连接
为true 自动连接,只要蓝牙设备变得可用
实测发现,用false连接比较好,比较快, true会等个十几秒甚至几分钟才会连接上。 开发过程中一般都是用false,扫描到bluetoothdevice之后,直接用false连接即可。
BluetoothGattCallback 是非常重要的回调函数,手机与蓝牙设备的一切通信结果都在这里体现。待会细讲。
BluetoothGattService
蓝牙设备所拥有的服务。在这里比喻成 班级 吧。bluetoothdevice就比喻成学校吧。 一个学校可以有很多个班级。班级 根据UUID来区别。
BluetoothGattCharacteristic
蓝牙设备所拥有的特征。比喻成 学生。 一个班级里也可以有很多个学生。学生也是根据UUID 来区别
当你需要用手机来和蓝牙设备通信的时候,就相当于 你想 和一个学生交流,你得先知道 这个学生的学号,所在班级号,就是开发中的所说的CharacUUID, ServiceUUID
BluetoothGattCallback
所有操作的回调函数。
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
//收到设备notify值 (设备上报值)
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
//读取到值
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//write成功(发送值成功)
}
}
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothGatt.STATE_CONNECTED) {
// 连接成功
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
// 断开连接
}
}
}
@Override
public void onDescriptorRead(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//获取到RSSI, RSSI 正常情况下 是 一个 负值,如 -33 ; 这个值的绝对值越小,代表设备离手机越近
//通过mBluetoothGatt.readRemoteRssi();来获取
}
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//寻找到服务
}
}
};
---------------------------------------------------------------------------------------------------------------------------------------
当调用了连接函数 mBluetoothGatt = bluetoothDevice.connectGatt(this.context, false, gattCallback);之后,
如果连接成功就会 走到 连接状态回调:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//首先判断这个status 如果等于 BluetoothGatt.GATT_SUCCESS(value=0)代表这个回调是正常的,
//如果不等于 0,那边就代表没有成功,也不需要进行其他操作了,
// 连接成功和断开连接都会走到这里
if (newState == BluetoothGatt.STATE_CONNECTED) {
// 连接成功
//连接成功之后,我们应该立刻去寻找服务(上面提到的 BluetoothGattService ),只有寻找到服务之后,才可以和设备进行通信
gatt.discoverServices();// 寻找服务
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
// 断开连接
}
}
}
当判断到连接成功之后,会去寻找服务, 这个过程是异步的,会耗点时间,当寻找到服务之后,会走到回调:
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//寻找到服务
//寻找服务之后,我们就可以和设备进行通信,比如下发配置值,获取设备电量什么的
readBatrery(); //读取电量操作
sendSetting(); //下发配置值
}
}
/***读操作***/
void readBatrery(){
//如上面所说,想要和一个学生通信,先知道他的班级(ServiceUUID)和学号(CharacUUID)
BluetoothGattService batteryService=mBluetoothGatt.getService(UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb")); //此处的0000180f...是举例,实际开发需要询问硬件那边
if(batteryService!=null){
BluetoothGattCharacteristic batteryCharacteristic=batteryService.getCharacteristic(UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb")); //此处的 00002a19 ...是举例,实际开发需要询问硬件那边
if(batteryCharacteristic!=null){
mBluetoothGatt.readCharacteristic(batteryCharacteristic); //读取电量, 这是读取batteryCharacteristic值的方法,读取其他的值也是如此,只是它们的ServiceUUID 和CharacUUID不一样
}
}
}
如果读取电量(或者读取其他值)成功之后 ,会来到 回调:
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
//读取到值,根据UUID来判断读到的是什么值
if (characteristic.getUuid().toString()
.equals("00002a19-0000-1000-8000-00805f9b34fb")) {// 获取到电量
int battery = characteristic.getValue()[0];
}
}
/***写操作***/
void sendSetting(){
BluetoothGattService sendService=mBluetoothGatt.getService(UUID.fromString("00001805-0000-1000-8000-00805f9b34fb"));//此处的00001805...是举例,实际开发需要询问硬件那边
if(sendService!=null){
BluetoothGattCharacteristic sendCharacteristic=sendService.getCharacteristic(UUID.fromString("00002a08-0000-1000-8000-00805f9b34fb"));//此处的00002a08...是举例,实际开发需要询问硬件那边
if(sendCharacteristic!=null){
sendCharacteristic.setValue(new byte[] { 0x01,0x20,0x03 });//随便举个数据
mBluetoothGatt.writeCharacteristic(sendCharacteristic);//写命令到设备,
}
}
}
如果下发配置成功之后,会来到回调:
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//write成功(发送值成功),可以根据 characteristic.getValue()来判断是哪个值发送成功了,比如 连接上设备之后你有一大串命令需要下发,你调用多次写命令, 这样你需要判断是不是所有命令都成功了,因为android不太稳定,有必要来check命令是否成功,否则你会发现你明明调用 写命令,但是设备那边不响应
}
}
讲解完读写操作,还有一个重要操作 就是 Notify(通知)
notify 就是让设备 可以发送通知给你,也可以说上报值给你(发送命令给你)
首先你得打开设备的通知功能: // 参数 enable 就是打开还是关闭, characteristic就是你想让具有通知功能的BluetoothGattCharacteristic
private boolean enableNotification(boolean enable,
BluetoothGattCharacteristic characteristic) {
if (mBluetoothGatt == null || characteristic == null)
return false;
if (!mBluetoothGatt.setCharacteristicNotification(characteristic,
enable))
return false;
BluetoothGattDescriptor clientConfig = characteristic
.getDescriptor(UUIDUtils.CCC);
if (clientConfig == null)
return false;
if (enable) {
clientConfig
.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
} else {
clientConfig
.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
}
return mBluetoothGatt.writeDescriptor(clientConfig);
}
一旦设备那边notify 数据给你,你会在回调里收到:
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
//收到设备notify值 (设备上报值),根据 characteristic.getUUID()来判断是谁发送值给你,根据 characteristic.getValue()来获取这个值
}
=========================以上属于BLE基本的操作,下面是进阶========================================
1. 关于发送命令, 比如刚连接上设备,你会把很多配置都发送给设备,此时可能会调用多次 write操作,这个时候你应该需要在 write操作之间 加点间隔 例如开启线程,用 Thread.sleep(150), 因为只有收到 onCharacteristicWrite回调之后才代表你的值成功了,如果
不加间隔,你会发现可能只会成功一个值。目前测试下来,我才用的是间隔150毫秒比较好,不会太慢,成功率也会高, 可自己实践慢慢调试,这个数值还与设备端update速率有关
在Write 操作的时候,还有个办法可以增快下发速度,且不需要加sleep
就是设置 characteristic .setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); Wrtite characteristic without requiring a response by the remote device, 就是不需 要回复,这样速度会快,测试下来连续发送9个数据,设备端那边打印数据都收到且是正确的,但回调值有点奇怪:
图1 是我的发送指令 ,连续无间隔发送9个命令:
图2,3是 onCharacteristicWrite 回调打印信息:
从图片看出,有的值没有收到 onCharacteristicWrite 回调,如第一个值 { 1, 0x08, 3 } ,有的值回调了2次 如 { 1, 0x04, 3 },可能会觉得有的值没有发送成功吧?
以下是 设备端收到值的打印信息:(0x00FF是用做分隔符,可忽略)
可以发现,9个值均接收正常, 所以说 这种快速发值,还是有点可靠的。测试的不多,各位可斟酌使用
2.关于重连
提到ble,估计想的最多的就是重连了。重连当然得是自动重连。可能有人想到的的就是 断开之后再来一次bluetoothDevice.connectGatt(this.context, false, gattCallback)行不行?
先看看官方的说法 :
官方说 mbluetoothGatt.connect()方法就是用来重连的, 只要你断线之后 调用此方法就好了。
测试下来 确实可以,但不同手机体验不一样, 但大家知道 android 碎片化现象很严重,什么 flyme,什么MIUI,emotion之类的。所以需要做什么适配。
只调用 mbluetoothGatt.connect()来重连,我归类为2种 三星 手机 和 非三星手机。
三星手机现象:
重连非常迅速 ,断开之后,只要一回到有效范围内。立马重连成功。 就算隔一天你回来 靠近设备,立马连接成功。这简直就是太棒了。
非三星手机现象:
也会重连成功,但这时间无法保证,有可能1分钟,有可能5分钟,10分钟,甚至就连不上了。体验非常不好。
说一下 解决办法 :
判断手机是否为三星手机:
private boolean checkIsSamsung() { // 此方法是我自行使用众多三星手机总结出来,不一定很准确
String brand = android.os.Build.BRAND;
Log.e("", " brand:" + brand);
if (brand.toLowerCase().equals("samsung")) {
return true;
}
return false;
}
在设备断线之后, 我们需要进入重连流程:
if(isSamsung){
//这里最好 Sleep 300毫秒,测试发现有时候三星手机断线之后立马调用connect会容易蓝牙奔溃
mbluetoothGatt.connect();
}else{
connectGatt();
handler.removeCallbacks(runnableReconnect );
handler.postDelayed(runnableReconnect , 30*1000);
}
流程就是断线之后 三星手机 调用官方的connect()函数,而非三星手机 使用 connectGatt() ,相当于重新建立连接,并且每30s去循环询问是否需要重新建立连接,如果已经连接好,取消这个循环。这样能保证过一天回来你也有机会重连上,非三星手机通过这个方法重连体验特别好,也可以达到回来之后瞬间连接的效果
这样的方案目前是比较好的,手机APP也可以很省电。 ,像很多其他的防丢器类APP,比如nut.研究过这个app,这个软件总是在扫描,一直scan,不管你连上还是不连上。 android的scan是很耗电的,app功能完善之后同时也要考虑电量优化。
这里测试过 手机的耗电量,用示波器来接手机测试的,
三星断线之后 重连的阶段,耗电量大概在20ma-30ma(锁屏电量 3ma),
魅族MX2 在30s循环期间, 耗电量大概在15ma(锁屏电量 15ma),
这里推测三星的connect()期间它系统自己在后台使用低频率的扫描,这样可以使他重连是很快的。scan就要耗电,所以它电量会比锁屏上升一点。
魅族MX2采用每30s重新建立连接,没有scan,基本没有额外耗电。
这种 重连流程 用身边的手机 三星S3,S4,note2,MX2,M3,Nexus5测试过都很好用。亲们也可以用其他测试一下。
public void connectGatt() {
if (bluetoothDevice != null) {
if (mBluetoothGatt != null) {
mBluetoothGatt.close();
mBluetoothGatt.disconnect();
mBluetoothGatt = null;
}
mBluetoothGatt = bluetoothDevice.connectGatt(this.context, false,
gattCallback);
} else {
Log.e(tagString,
"the bluetoothDevice is null, please reset the bluetoothDevice");
}
}
Runnable runnableReconnect = new Runnable() {
@Override
public void run() {
if (! isConnected() ) {
handler.postDelayed(this, 30 * 1000);
connectGatt();
}
}
};
autoConnect 为false 立刻发起一次连接
为true 自动连接,只要蓝牙设备变得可用
实测发现,用false连接比较好,比较快, true会等个十几秒甚至几分钟才会连接上。 开发过程中一般都是用false,扫描到bluetoothdevice之后,直接用false连接即可。
BluetoothGattCallback 是非常重要的回调函数,手机与蓝牙设备的一切通信结果都在这里体现。待会细讲。
BluetoothGattService
蓝牙设备所拥有的服务。在这里比喻成 班级 吧。bluetoothdevice就比喻成学校吧。 一个学校可以有很多个班级。班级 根据UUID来区别。
BluetoothGattCharacteristic
蓝牙设备所拥有的特征。比喻成 学生。 一个班级里也可以有很多个学生。学生也是根据UUID 来区别
当你需要用手机来和蓝牙设备通信的时候,就相当于 你想 和一个学生交流,你得先知道 这个学生的学号,所在班级号,就是开发中的所说的CharacUUID, ServiceUUID
BluetoothGattCallback
所有操作的回调函数。
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
//收到设备notify值 (设备上报值)
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
//读取到值
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//write成功(发送值成功)
}
}
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothGatt.STATE_CONNECTED) {
// 连接成功
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
// 断开连接
}
}
}
@Override
public void onDescriptorRead(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//获取到RSSI, RSSI 正常情况下 是 一个 负值,如 -33 ; 这个值的绝对值越小,代表设备离手机越近
//通过mBluetoothGatt.readRemoteRssi();来获取
}
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//寻找到服务
}
}
};
---------------------------------------------------------------------------------------------------------------------------------------
当调用了连接函数 mBluetoothGatt = bluetoothDevice.connectGatt(this.context, false, gattCallback);之后,
如果连接成功就会 走到 连接状态回调:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//首先判断这个status 如果等于 BluetoothGatt.GATT_SUCCESS(value=0)代表这个回调是正常的,
//如果不等于 0,那边就代表没有成功,也不需要进行其他操作了,
// 连接成功和断开连接都会走到这里
if (newState == BluetoothGatt.STATE_CONNECTED) {
// 连接成功
//连接成功之后,我们应该立刻去寻找服务(上面提到的 BluetoothGattService ),只有寻找到服务之后,才可以和设备进行通信
gatt.discoverServices();// 寻找服务
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
// 断开连接
}
}
}
当判断到连接成功之后,会去寻找服务, 这个过程是异步的,会耗点时间,当寻找到服务之后,会走到回调:
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//寻找到服务
//寻找服务之后,我们就可以和设备进行通信,比如下发配置值,获取设备电量什么的
readBatrery(); //读取电量操作
sendSetting(); //下发配置值
}
}
/***读操作***/
void readBatrery(){
//如上面所说,想要和一个学生通信,先知道他的班级(ServiceUUID)和学号(CharacUUID)
BluetoothGattService batteryService=mBluetoothGatt.getService(UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb")); //此处的0000180f...是举例,实际开发需要询问硬件那边
if(batteryService!=null){
BluetoothGattCharacteristic batteryCharacteristic=batteryService.getCharacteristic(UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb")); //此处的 00002a19 ...是举例,实际开发需要询问硬件那边
if(batteryCharacteristic!=null){
mBluetoothGatt.readCharacteristic(batteryCharacteristic); //读取电量, 这是读取batteryCharacteristic值的方法,读取其他的值也是如此,只是它们的ServiceUUID 和CharacUUID不一样
}
}
}
如果读取电量(或者读取其他值)成功之后 ,会来到 回调:
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
//读取到值,根据UUID来判断读到的是什么值
if (characteristic.getUuid().toString()
.equals("00002a19-0000-1000-8000-00805f9b34fb")) {// 获取到电量
int battery = characteristic.getValue()[0];
}
}
/***写操作***/
void sendSetting(){
BluetoothGattService sendService=mBluetoothGatt.getService(UUID.fromString("00001805-0000-1000-8000-00805f9b34fb"));//此处的00001805...是举例,实际开发需要询问硬件那边
if(sendService!=null){
BluetoothGattCharacteristic sendCharacteristic=sendService.getCharacteristic(UUID.fromString("00002a08-0000-1000-8000-00805f9b34fb"));//此处的00002a08...是举例,实际开发需要询问硬件那边
if(sendCharacteristic!=null){
sendCharacteristic.setValue(new byte[] { 0x01,0x20,0x03 });//随便举个数据
mBluetoothGatt.writeCharacteristic(sendCharacteristic);//写命令到设备,
}
}
}
如果下发配置成功之后,会来到回调:
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//write成功(发送值成功),可以根据 characteristic.getValue()来判断是哪个值发送成功了,比如 连接上设备之后你有一大串命令需要下发,你调用多次写命令, 这样你需要判断是不是所有命令都成功了,因为android不太稳定,有必要来check命令是否成功,否则你会发现你明明调用 写命令,但是设备那边不响应
}
}
讲解完读写操作,还有一个重要操作 就是 Notify(通知)
notify 就是让设备 可以发送通知给你,也可以说上报值给你(发送命令给你)
首先你得打开设备的通知功能: // 参数 enable 就是打开还是关闭, characteristic就是你想让具有通知功能的BluetoothGattCharacteristic
private boolean enableNotification(boolean enable,
BluetoothGattCharacteristic characteristic) {
if (mBluetoothGatt == null || characteristic == null)
return false;
if (!mBluetoothGatt.setCharacteristicNotification(characteristic,
enable))
return false;
BluetoothGattDescriptor clientConfig = characteristic
.getDescriptor(UUIDUtils.CCC);
if (clientConfig == null)
return false;
if (enable) {
clientConfig
.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
} else {
clientConfig
.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
}
return mBluetoothGatt.writeDescriptor(clientConfig);
}
一旦设备那边notify 数据给你,你会在回调里收到:
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
//收到设备notify值 (设备上报值),根据 characteristic.getUUID()来判断是谁发送值给你,根据 characteristic.getValue()来获取这个值
}
=========================以上属于BLE基本的操作,下面是进阶========================================
1. 关于发送命令, 比如刚连接上设备,你会把很多配置都发送给设备,此时可能会调用多次 write操作,这个时候你应该需要在 write操作之间 加点间隔 例如开启线程,用 Thread.sleep(150), 因为只有收到 onCharacteristicWrite回调之后才代表你的值成功了,如果
不加间隔,你会发现可能只会成功一个值。目前测试下来,我才用的是间隔150毫秒比较好,不会太慢,成功率也会高, 可自己实践慢慢调试,这个数值还与设备端update速率有关
在Write 操作的时候,还有个办法可以增快下发速度,且不需要加sleep
就是设置 characteristic .setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); Wrtite characteristic without requiring a response by the remote device, 就是不需 要回复,这样速度会快,测试下来连续发送9个数据,设备端那边打印数据都收到且是正确的,但回调值有点奇怪:
图1 是我的发送指令 ,连续无间隔发送9个命令:
![](http://a1.eoeandroid.com/attachment/forum/201501/30/145419gcychzd6d3x6refk.png)
图2,3是 onCharacteristicWrite 回调打印信息:
![](http://a1.eoeandroid.com/attachment/forum/201501/30/145605zynj0ju6p6t9j0jj.png)
![](http://a1.eoeandroid.com/attachment/forum/201501/30/145609m33wkg4639n561a4.png)
从图片看出,有的值没有收到 onCharacteristicWrite 回调,如第一个值 { 1, 0x08, 3 } ,有的值回调了2次 如 { 1, 0x04, 3 },可能会觉得有的值没有发送成功吧?
以下是 设备端收到值的打印信息:(0x00FF是用做分隔符,可忽略)
![](http://a1.eoeandroid.com/attachment/forum/201501/30/150818e873cmzsmm3csic3.png)
![](http://a1.eoeandroid.com/attachment/forum/201501/30/150820zlus3tvuws488prw.png)
可以发现,9个值均接收正常, 所以说 这种快速发值,还是有点可靠的。测试的不多,各位可斟酌使用
2.关于重连
提到ble,估计想的最多的就是重连了。重连当然得是自动重连。可能有人想到的的就是 断开之后再来一次bluetoothDevice.connectGatt(this.context, false, gattCallback)行不行?
先看看官方的说法 :
![](http://a1.eoeandroid.com/attachment/forum/201501/30/151717a32l28z62lerelm3.png)
官方说 mbluetoothGatt.connect()方法就是用来重连的, 只要你断线之后 调用此方法就好了。
测试下来 确实可以,但不同手机体验不一样, 但大家知道 android 碎片化现象很严重,什么 flyme,什么MIUI,emotion之类的。所以需要做什么适配。
只调用 mbluetoothGatt.connect()来重连,我归类为2种 三星 手机 和 非三星手机。
三星手机现象:
重连非常迅速 ,断开之后,只要一回到有效范围内。立马重连成功。 就算隔一天你回来 靠近设备,立马连接成功。这简直就是太棒了。
非三星手机现象:
也会重连成功,但这时间无法保证,有可能1分钟,有可能5分钟,10分钟,甚至就连不上了。体验非常不好。
说一下 解决办法 :
判断手机是否为三星手机:
private boolean checkIsSamsung() { // 此方法是我自行使用众多三星手机总结出来,不一定很准确
String brand = android.os.Build.BRAND;
Log.e("", " brand:" + brand);
if (brand.toLowerCase().equals("samsung")) {
return true;
}
return false;
}
在设备断线之后, 我们需要进入重连流程:
if(isSamsung){
//这里最好 Sleep 300毫秒,测试发现有时候三星手机断线之后立马调用connect会容易蓝牙奔溃
mbluetoothGatt.connect();
}else{
connectGatt();
handler.removeCallbacks(runnableReconnect );
handler.postDelayed(runnableReconnect , 30*1000);
}
流程就是断线之后 三星手机 调用官方的connect()函数,而非三星手机 使用 connectGatt() ,相当于重新建立连接,并且每30s去循环询问是否需要重新建立连接,如果已经连接好,取消这个循环。这样能保证过一天回来你也有机会重连上,非三星手机通过这个方法重连体验特别好,也可以达到回来之后瞬间连接的效果
这样的方案目前是比较好的,手机APP也可以很省电。 ,像很多其他的防丢器类APP,比如nut.研究过这个app,这个软件总是在扫描,一直scan,不管你连上还是不连上。 android的scan是很耗电的,app功能完善之后同时也要考虑电量优化。
这里测试过 手机的耗电量,用示波器来接手机测试的,
三星断线之后 重连的阶段,耗电量大概在20ma-30ma(锁屏电量 3ma),
魅族MX2 在30s循环期间, 耗电量大概在15ma(锁屏电量 15ma),
这里推测三星的connect()期间它系统自己在后台使用低频率的扫描,这样可以使他重连是很快的。scan就要耗电,所以它电量会比锁屏上升一点。
魅族MX2采用每30s重新建立连接,没有scan,基本没有额外耗电。
这种 重连流程 用身边的手机 三星S3,S4,note2,MX2,M3,Nexus5测试过都很好用。亲们也可以用其他测试一下。
public void connectGatt() {
if (bluetoothDevice != null) {
if (mBluetoothGatt != null) {
mBluetoothGatt.close();
mBluetoothGatt.disconnect();
mBluetoothGatt = null;
}
mBluetoothGatt = bluetoothDevice.connectGatt(this.context, false,
gattCallback);
} else {
Log.e(tagString,
"the bluetoothDevice is null, please reset the bluetoothDevice");
}
}
Runnable runnableReconnect = new Runnable() {
@Override
public void run() {
if (! isConnected() ) {
handler.postDelayed(this, 30 * 1000);
connectGatt();
}
}
};