Ble4.0 APP开发总结
最近自学了一个多礼拜的蓝牙ble开发,对这方面有些粗略的理解,及时做个总结,不对的地方希望大家帮忙揪出来。
首先介绍几个类 :BluetoothGatt,BluetoothGattService,BluetoothGattCharacteristic,BluetoothGattCallback
BluetoothGatt
这个类是开发里最重要最常用到的东西了,我把它理解为 手机与 设备通信的管道。有了这个管道,收发数据就容易了。
BluetoothGatt 是通过蓝牙设备连接获得:
bluetoothDevice.connectGatt(this.context, false, gattCallback);
先来谈一下这个方法:
android.bluetooth.BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)
第一个参数context不说
第二个autoConnect
为false 立刻发起一次连接为true 自动连接,只要蓝牙设备变得可用实测发现,用false连接比较好,比较快, true会等个十几秒甚至几分钟才会连接上。 开发过程中一般都是用false,扫描到bluetoothdevice之后,直接用false连接即可。
第三个BluetoothGattCallback
是非常重要的回调函数,手机与蓝牙设备的一切通信结果都在这里体现。待会仔细分析。
BluetoothGattService
蓝牙设备所拥有的服务。在这里比喻成 班级 吧。bluetoothdevice就比喻成学校吧。 一个学校可以有很多个班级。班级 根据UUID来区别。
BluetoothGattCharacteristic
蓝牙设备所拥有的特征。比喻成 学生。一个班级里也可以有很多个学生。学生也是根据UUID 来区别
当你需要用手机来和蓝牙设备通信的时候,就相当于 你想 和一个学生交流,你得先知道 这个学生的学号,所在班级号,就是开发中的所说的CharacUUID, ServiceUUID。这个uuid就是相当于网络中的端口号,蓝牙的mac地址就是相当于网络中的ip地址,只不过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(); //下发配置值
}
}
/***
* 读操作
***/
public 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) {
//读取电量, 这是读取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()来获取这个值
}
关于发送命令
比如刚连接上设备,你会把很多配置都发送给设备,此时可能会调用多次 write操作,这个时候你应该需要在 write操作之间 加点间隔 例如开启线程,用 Thread.sleep(150), 因为只有收到onCharacteristicWrite回调之后才代表你的值成功了,如果不加间隔,你会发现可能只会成功一个值。目前测试下来,我才用的是间隔150毫秒比较好,不会太慢,成功率也会高, 可自己实践慢慢调试,这个数值还与设备端update速率有关在Write 操作的时候,还有个办法可以增快下发速度,且不需要加sleep就是设置:
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
就是不需要回复,这样速度会快,还是有点可靠的。各位可斟酌使用