赞
踩
简单的说这个UUID是蓝牙通信必须要用的唯一识别号。
复杂的说是服务记录的UUID是用于查找RFCOMM通道。
那什么是RFCOMM?它是为了兼容传统的串口应用,同时取代有线的通信方式,蓝牙协议栈需要提供与有线串口一致的通信接口而开发出的协议。
经典蓝牙泛指支持蓝牙协议4.0以下的蓝牙,一般用于连续流式传输音频和数据量比较大的传输,例如音乐、语音、打印机等。
英文名称:Bluetooth Low Energy,简称(ble),低功耗蓝牙指支持蓝牙协议4.0或更高的版本,它不向后兼容4.0之前的经典蓝牙协议,目前已经迭代到蓝牙5.3版本,主打低功耗(使用一个纽扣电池最起码都能工作好几个月),低延迟(几毫秒级别的响应);应用于实时性要求比较高,但是低速率,低功耗的场景,如鼠标键盘、智能家居、智能穿戴这类不需要大数据量交互的场景中,非常适合物联网应用。
英文单词:Universally Unique Identifier,用于标识蓝牙服务以及通讯特征访问属性,蓝牙技术联盟SIG定义UUID 共用了一个基本的UUID ,这些共用的UUID大致有下面几类。注意,设备厂商可以使用共用的UUID,也可以自定义,如果自定义需要向开发者提供。
分为两种,一种是安全的连接,一种是不安全的连接。
(1)安全的连接
对应的UUID是:fa87c0d0-afac-11de-8a39-0800200c9a66
此UUID将作为此函数的参数:createRfcommSocketToServiceRecord
(2)不安全的连接
是因为通信通道将没有经过身份验证的链接密钥,即它将受到中间人攻击。
对应的UUID是:8ce255c0-200a-11e0-ac64-0800200c9a66
此UUID将作为此函数的参数:createInsecureRfcommSocketToServiceRecord
如:小型蓝牙打印机
UUID1:0000180a-0000-1000-8000-00805f9b34fb
或
UUID2:00001101-0000-1000-8000-00805F9B34FB
如:一些物联网终端设备。
低功耗设备需要的UUID则更为不同也更细分出来,需要用到三个不同的UUID,即用于服务连接的UUID、读取数据的UUID、写入数据的UUID,这些一般对接的蓝牙设备厂商也会提供此三项值。
//服务连接的UUID
SERVICE_UUID = “0000ffb0-0000-1000-8000-00805f9b34fb”;
//读使用的UUID
READ_UUID = “0000ffb2-0000-1000-8000-00805f9b34fb”;
//写使用的UUID
WRITE_UUID = “0000ffb1-0000-1000-8000-00805f9b34fb”;
当然我们也可以借助"BLE调试助手"这款APP来连接蓝牙设备,并查看对应的三项UUID,如下图所示:
虽然它们在开发时,都使用了android.bluetooth.BluetoothDevice这个类,但是却使用了不同的连接函数,如下。
Added in API level 18
BluetoothDevice.connectGatt(context, false, bluetoothGattCallback)
Added in API level 5,有两种连接方式,
第一种方式:
BluetoothSocket socket = BluetoothDevice.createRfcommSocketToServiceRecord(GlobalDef.BT_UUID);
socket.connect();
第二种方式:
BluetoothSocket socket = BluetoothDevice.createInsecureRfcommSocketToServiceRecord(GlobalDef.BT_UUID);
socket.connect();
不同的连接函数,导致了开发过程中使用的蓝牙API也已完全不同。所以,寻找Demo时一定要区分是低功耗蓝牙开发还是经典蓝牙开发,不然很可能出现,用传统的蓝牙的开发方式去连接低功耗蓝牙设备导致连接失败等一些列诡异的问题。
现象:当蓝牙设备处于忙碌状态时,发送数据此项操作将会失败。
为什么蓝牙设备会处于忙碌状态呢?
查看源码发现了一些线索,在android.bluetooth.BluetoothGatt类的writeCharacteristic函数中有一段同步的代码,synchronized锁住了mDeviceBusy变量,如果写入操作太快太猛,蓝牙设备某项任务还未执行完,将可能会导致此处直接返回false,也就是写入失败,不再继续往下执行。
package android.bluetooth; public final class BluetoothGatt implements BluetoothProfile { ... private Boolean mDeviceBusy = false; ... public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { ... synchronized (mDeviceBusy) { if (mDeviceBusy) return false; mDeviceBusy = true; } ... mService.writeCharacteristic(mClientIf, device.getAddress(), ... } }
那么如何去判定忙碌状态呢?
//(检查蓝牙设备是否处于忙碌状态)此函数执行的超时时间 private static long WAIT_CHECK_DEVICE_BUSY_TIME = 2000; /** * 检查蓝牙设备是否处于忙碌状态 * @param mBluetoothGatt */ private void checkDeviceBusyStatus(BluetoothGatt mBluetoothGatt) { boolean isBusy; long startTime = System.currentTimeMillis(); try { while (System.currentTimeMillis() - startTime < WAIT_CHECK_DEVICE_BUSY_TIME) { isBusy = (boolean) readField(mBluetoothGatt, "mDeviceBusy"); if (isBusy) { //如果蓝牙设备处于忙碌状态,则休息10毫秒 Thread.sleep(10); Log.d(TAG, "设备是否忙碌状态:" + isBusy); } else { break; } } } catch (Exception e) { e.printStackTrace(); } } /** * 通过反射的方式去获取对象私有属性的值 */ private Object readField(Object obj, String fieldName) throws IllegalAccessException, NoSuchFieldException { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); return field.get(obj); }
在写入数据前调用checkDeviceBusyStatus函数:
//检查设备是否忙碌的状态
checkDeviceBusyStatus(mBluetoothGatt);
bool writeResult= mBluetoothGatt.writeCharacteristic(writeCharacteristic);
writeCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
WRITE_TYPE_DEFAULT 写入数据后默认远程设备有响应;
WRITE_TYPE_NO_RESPONSE 写入数据后,无需远程设备响应,速度更快。
(1)蓝牙开发的版本不对
一定要向设备厂商明确使用的蓝牙协议的版本号是多少,以决定采用那种开发方式,采用(BLE)低功耗蓝牙开发的方式去连接低功耗的蓝牙设备,经典蓝牙设备开发的方式连接经典蓝牙设备,如果要想都兼容 ,可通过蓝牙版本区分来调用对应的版本的代码。
(2)UUID不正确
这类问题最常见,明确UUID是使用的共用的UUID还是自定义的UUID,如果设备厂商自定义了UUID,那么就让设备厂商提供,UUID不正确将会导致一直连接失败。
参考:
[1]:低功耗蓝牙(BLE)你入门了吗
[2]:蓝牙服务UUID
原创不易,求个关注。
微信公众号:一粒尘埃的漫旅
里面有很多想对大家说的话,就像和朋友聊聊天。
写代码,做设计,聊生活,聊工作,聊职场。
我见到的世界是什么样子的?
搜索关注我吧。
公众号与博客的内容不同。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。