最近在项目中需要实现一个蓝牙摇一摇开门的功能,蓝牙和门口机数据交互的协议已经给定,考虑到蓝牙的适配性,决定使用低功耗蓝牙进行开发。低功耗蓝牙是Android 4.3开始支持的,其主要特点是主要特点是快速搜索,快速连接,超低功耗保持连接和数据传输,常被用在可穿戴设备中。
1. 开发之前的准备工作
在进行BLE(Bluetooth Low Energy)开发之前,我们先来了解一波BLE数据传输涉及到的关键概念:
Generic Attribute Profile (GATT)
通过BLE连接,读写属性类小数据的Profile通用规范。现在所有的BLE应用Profile都是基于GATT的。
Attribute Protocol (ATT)
GATT是基于ATT Protocol的。ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据。
每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。
Characteristic
Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。
Descriptor
对Characteristic的描述,例如范围、计量单位等。
Service
Characteristic的集合。例如一个service叫做“Heart Rate Monitor”,
它可能包含多个Characteristics,其中可能包含一个叫做“heart rate measurement"的Characteristic。
2. 开发需要的权限
Android使用蓝牙,需要声明BLUETOOTH权限,如果需要扫描设备或者操作蓝牙设置,则还需要BLUETOOTH_ADMIN权限,如果Android版本在5.0以上,蓝牙还需要位置权限。
1.png3. 蓝牙的开启和蓝牙状态的监听
在蓝牙编程中,首先我们要知道如何去开启和关闭蓝牙,下面介绍两种方法:
1) 通过获取蓝牙管理服务和适配器来开启、关闭蓝牙,获取当前蓝牙是否为可用状态;
2.png2) 跳转到系统蓝牙设置界面,由用户自行开启蓝牙,并通过广播监听蓝牙开启与关闭的通知,通过蓝牙适配器来获取当前蓝牙是否为可用状态:
跳转蓝牙设置界面
3.png注册蓝牙状态广播监听
4.png蓝牙广播监听,可以接受蓝牙开启,关闭时的通知:
5.png蓝牙是否开启的判断
6.png第一种方法可以用来直接开关蓝牙,如果是Android 6.0系统及以上的还需要添加动态申请权限代码;第二种方法是引导用户进入设置界面进行手动设置,再通过监听开关状态来执行后续操作,不需要动态申请权限;
4. 蓝牙设备的扫描和连接
1)启动蓝牙后,我们就要开始使用蓝牙进行扫描,发现我们想要连接的设备。在Android5.0版本时,Google对BLE更新了API,添加了新的扫描方法和回调,在下面代码中会进行区分:
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
开始扫描
if(Build.VERSION.SDK_INT >= 21){
mBluetoothLeScanner.startScan(mScanCallback);
}else{
mBluetoothAdapter.startLeScan(mLeScanCallback);
}
关闭扫描
if(Build.VERSION.SDK_INT >= 21){
mBluetoothLeScanner.stopScan(mScanCallback);
}else{
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
扫描回调也不相同
7.png2) 扫描找到想要连接的设备后,就可以开始进行设备连接了:
8.png注意:根据需要决定是否对有连接过的设备进行重新连接。比如有些设备在连接通讯后就自行断开,连接总时长只有几秒钟的,这样的不需要保留地址,每次都直接连接比较好。
连接设备时,会触发一个通信的回调,这个回调比较重要,在设备连接,数据通信中都会用到:
9.png在连接状态回调中判断连接状态,连接成功后,调用gatt.discoverServices()方法去发现连接的设备提供的服务及其特征和描述符;发现成功后,会调用onServicesDiscovered()回调,表示可以与之通信了;
10.png发现服务后,通过handler回调到主线程,执行了下面的方法用来设置后续需要用到连接的服务节点下的Characteristic发生改变时进行通知:
11.png此处是获取了开门服务,设置开门服务下接受到命令返回的Characteristic
12.pngsetCharacteristicNotification()方法会触发onCharacteristicChanged()回调。接受到onCharacteristicChanged()回调后证明可以设置通知成功,可以开始发送通讯指令了。
13.png5. 蓝牙的数据发送和接收
经过4中的步骤后,此时远程设备已经准备好进行蓝牙通讯了,下面开始发送指令:
14.png此处是向开门服务下的接收命令的BluetoothGattCharacteristic对象写入命令数据。
15.pngwriteCharacteristic()方法会触发onCharacteristicWrite()回调
16.png注意:BLE向设备写入数据时,最大20byte,如果需要写入的数据大于20byte,可以选择将数据进行分割,先发送前20byte,利用回调触发将后续数据依次发送,每次最多20byte。
远程设备接收到完整指令并执行后,特征值变化会触发onCharacteristicChanged()回调,并传回数据
17.png这样,一个完整的通信循环就完成了。
6. 总结
蓝牙BLE本身的流程并不复杂,关键在于存在有许多特殊情况需要处理,下面对特殊情况的处理进行叙述和总结:
1、 调用gatt.discoverServices()方法后不执行onServiceDiscovered()回调,这种问题有两种情况,一种是设备距离远,回调时间过长,一种是并没有真正连接到设备,可以尝试将gatt.discoverServices()方法放到主线程中执行;
2、 onServicesDiscovered 回调里不能直接执行 write /readDataFromCharacteristic() 或者 enableNotificationOfCharacteristic之类的,而要放到主线程里执行,可以使用 handler转换线程;
3、 扫描尽量不要放在主线程进行,可以放入子线程中,部分机型会出现do too many work in main thread。
4、 任何出错,超时,用完就马上调用Gatt.disconnect(), Gatt.close()。
网友评论