1.前言
随着智能穿戴的普及,蓝牙开发也火热起来。不过与传统蓝牙开发不一样的是,由于考虑到穿戴设备的电量问题和使用场景,即数据量小、离散传输和近距离通信,所以采用基于蓝牙4.0规范的BLE设备。
虽然在开发时调用的API有些不同,但流程大致相同,可以找到一些共通点,在此做个大概的归纳:
- 设置蓝牙权限,检测设备支持,启动蓝牙;
- 查找蓝牙设备(传统方式较为复杂,需先让远程设备可被检测,然后获取设备信息并配对,才能建立加密连接);
- 连接蓝牙设备(由于使用场景不同,所以协议也不同。传统方式使用Socket协议,需双方通过套接字识别,建立稳定的流式传输通道,通信量大但耗电。BLE则使用GATT协议,传输数据块,通过回调的方法进行读写和通知操作,通信量小、不连续但省电。)
- 解析通信数据,根据配置协议取出数据,交由业务逻辑判断,并给予内容的显示或操作上的回应。
- 关闭蓝牙设备,毕竟是很耗资源的功能,不用记得释放。
由此可知,不管开没开发过蓝牙,开发的是什么版本的蓝牙,其实不影响学习和理解BLE开发。
2.关键术语和概念
BLE,全称Bluetooth Low Energy,即低功耗蓝牙。除了远程设备支持外,安装应用的手机必须大于安卓4.3(API 18)版本才行。这方面的知识官网的开发指南上有明确的说明,若觉得英文不方便看,有人已经翻译了,下面主要是说一下关键点。
2.1.GATT协议
GATT分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。一个蓝牙4.0的终端可以包含多个Service,表示自己具有不同的功能;一个Service可以包含多个Characteristic,意味着一个功能由不同的特征共同描述;一个Characteristic包含一个Value和多个Descriptor,说明每个特征都是一个数值和多个帮助理解值含义的相关信息组成;一个Descriptor包含一个Value,类似XML文件中的
,是附加信息。需要注意的是,Characteristic和Descriptor是存放数据的地方,具有相关读写权限或者操作属性的设置。
2.2.角色和责任
设备的角色是固定的,中央设备具有扫描的功能,通过广播获取外围设备列表;外围设备则是数据采集的功能,并发出广播方便被搜索到。
但是,设备的责任是相对的,服务端作为数据的来源,而客户端则是获取数据的那一方,不管主动还是被动。
配置协议主要是告诉开发人员,数据的格式和内容,该如何读写。
3.位置权限
基本权限和设备支持判断就不讲了,大家看官方说明就行了。使用蓝牙时还需要获取位置信息,根据信息的来源和系统的版本有些不同的设置,具体如下描述:
- 5.0系统(API 21)之前,当信息仅来自于网络位置时,需添加权限
ACCESS_COARSE_LOCATION
。而当位置信息来自于GPS或来自于网络和GPS时,只需添加权限ACCESS_FINE_LOCATION
,系统会自动申请硬件功能。 - 而5.0系统之后,系统不再自动申请,需根据位置信息的来源分别添加如
android.hardware.location.network
或android.hardware.location.gps
相关硬件功能声明。
4.BluetoothAdapter
这个类映射了设备的蓝牙模块,蓝牙功能的使用将从它开始。
常用方法 | 作用解释 |
---|---|
getDefaultAdapter() | 获取蓝牙适配器,安卓4.3之后引入BluetoothManager,也可以通过它获取实例 |
getRemoteDevice(String address) | 远程设备是通过MAC地址识别的,通过这个方法获取实例 |
isEnabled() | 判断蓝牙是否打开,可通过enable()和disable()静默地切换状态,也可以通过Intent提示用户操作 |
startLeScan(BluetoothAdapter.LeScanCallback callback) | 开启LE蓝牙的搜索,另有方法对Service的UUID进行搜索 |
stopLeScan(BluetoothAdapter.LeScanCallback callback) | 关闭LE蓝牙的搜索,找到目标设备或到了设定时间就关闭 |
搜索BLE和传统蓝牙使用不同的方法,所以不能同时搜索两种设备。
5.LeScanCallback
传统蓝牙是通过BroadcastReceiver监听ACTION_FOUND
的意图获取每个搜索到的设备,而BLE则是通过回调这个类的方法。
private LeDeviceListAdapter mLeDeviceListAdapter;
...
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
6. BluetoothGatt
此对象是对GATT协议的封装,通过调用远程设备BluetoothDevice的connectGatt(Context, boolean, BluetoothGattCallback)
方法可以获取,布尔类型参数表示是否断后重连。由于是从远程设备处获取信息,所以远程设备是服务端而安卓设备是客户端。BluetoothGatt对象可对客户端进行相关操作。
常用方法 | 作用解释 |
---|---|
getDevice() | 获取GATT客户端连接的远程设备 |
getService(UUID uuid) | 获取远程设备提供的某个服务 |
getServices() | 获取远程设备提供的所以服务 |
close() | 关闭GATT客户端 |
connect() | 重连远程设备 |
disconnect() | 断开已建立的连接或取消正在尝试的连接 |
discoverServices() | 发现远程设备提供的服务以及它们的特性和描述 |
setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable) | 当指定特征的值变化时,是否发出通知/指示 |
readCharacteristic(BluetoothGattCharacteristic characteristic) | 从相关的远程设备读取所请求的特征 |
readDescriptor(BluetoothGattDescriptor descriptor) | 从相关的远程设备读取给定描述的值 |
writeCharacteristic(BluetoothGattCharacteristic characteristic) | 将指定的特征及值写入相关的远程设备 |
writeDescriptor(BluetoothGattDescriptor descriptor) | 将指定的描述值写入相关的远程设备 |
7.BluetoothGattCallback
BluetoothGatt对象的操作将会回调BluetoothGattCallback的相应方法来向用户反映结果,方便进一步的判断和操作。
new BluetoothGattCallback() {
// gatt为管理GATT客户端的对象
@Override // status为变化前状态,newState为变化后状态,由connect()和disconnect()方法引起
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
// TODO: 当GATT客户端与服务端连接状态发生改变时触发,执行连接状态相关业务
}
@Override // status为是否发现成功,由discoverServices()方法引起
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
// TODO: 当GATT客户端从服务端发现新的支持服务时触发,执行GATT数据解析,为读写更新提供对象
}
@Override // characteristic发生改变的特征,由setCharacteristicNotification()方法设置
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
// TODO: 当GATT服务端被指定的特征发生改变而发送通知时触发,更新UI或执行处理业务
}
@Override // characteristic读取的特征,status读取状态,由readCharacteristic()方法引起
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
// TODO: 当GATT客户端读取指定特征时触发,判断并执行显示或处理业务
}
@Override // characteristic写入的特征,status写入状态,由writeCharacteristic()方法引起
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
// TODO: 当GATT客户端向指定特征写入时触发,判断并检测写入的值是否正确
}
@Override // descriptor读取的描述,status读取状态,由readDescriptor()方法引起
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
// TODO: 当GATT客户端读取指定描述时触发,判断并执行显示或处理业务
}
@Override // descriptor写入的描述,status写入状态,由writeDescriptor()方法引起
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
// TODO: 当GATT客户端向指定描述写入时触发,判断并检测写入的值是否正确
}
};
}
8.关闭客户端
当你的应用不在需要BLE功能时,记得关闭并让系统释放资源。
public void close() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
9.总结
以上按照操作流程将涉及的关键类介绍了一遍,具体实现官方给出了Demo。大概讲一下思路,Activity展示数据和响应用户操作,BroadcastReceiver执行界面更新等具体业务,Service在Activity的控制下与BLE模块交互,详情参考这篇博客。
Characteristic和Descriptor的读取权限需通过位运算符的组合来设置,就是XX|XX
,大家肯定不陌生,相关分解识别代码可以参考这篇文章。
最后提供几个实际项目的博客链接供大家参考:
http://www.cnblogs.com/cxk1995/p/5693979.html
http://www.cnblogs.com/wobeinianqing/category/694014.html
http://www.cnblogs.com/heiyue/tag/bluetooth/default.html?page=15