Android BLE开发的基础知识

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.networkandroid.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

你可能感兴趣的:(Android BLE开发的基础知识)