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文件中的<tag>,是附加信息。需要注意的是,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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容