Android 蓝牙开发(一) – 传统蓝牙聊天室
Android 蓝牙开发(三) – 低功耗蓝牙开发
项目工程BluetoothDemo
前面已经学习了经典蓝牙开发,学习了蓝牙的配对连接和通信,又通过 配置 A2DP 文件,实现手机和蓝牙音响的连接和播放语音。
这篇,我们来学习蓝牙开发的最后一章,低功耗蓝牙 BLE,也就是我们常说的蓝牙 4.0 。
今天要完成的效果如下:


与传统蓝牙不同,低功耗蓝主要为了降低设备功耗,支持更低功耗(如心率检测仪,健身设备)等设备进行通信。
Android 在 4.3(API 18) 后将低功耗蓝牙内置,并提供对应的 API,以便于应用发现设备,查询服务和传输信息
低功耗蓝牙有两个角色,分别是中心设备和外围设备
- 外围设备:指功耗更低的设备,会不断的发出广播,直到与中心设备连接
- 中心设备:可以进行扫描,寻找外设广播,并从广播中拿到数据
一般我们的手机会充当中心设备,去搜索周围外设的广播,比如健康设备等,然后健康设备就是外围设备,一直发广播,直到中心设备连接上。在Android 5.0 后,手机也可以充当外围设备。
关于 BLE 的关键术语如下:
- 通用属性配置文件(GATT) : GATT 配置文件是一种通用规范,内容主要针对的是 BLE 通信读写时的简短的数据片段,目前 BLE 的通信均以 GATT 为基础
- 属性协议(ATT) : ATT 是 GATT 的基础,由它来传输属性和特征服务,这些属性都有一个特定的 UUID来作为唯一标识,为通信的基础。
- GATT Service : 通常中心设备与外围设备要进行,首先要知道服务的 UUID,并与之建立通信,然后通过特征和描述符等进行数据通信,这些等后面我们再来理解
首先,你需要使用 BLUETOOTH 的权限,考虑到 LE 信标通常与位置相关联,还须声明 ACCESS_FINE_LOCATION 权限。没有此权限,扫描将无法返回任何结果。
注意:如果您的应用适配 Android 9(API 级别 28)或更低版本,则您可以声明 ACCESS_COARSE_LOCATION 权限而非 ACCESS_FINE_LOCATION 权限。
注意!Android 10 需要你开启gps,否则蓝牙不可用
如果你想要你的设备只支持 BLE ,还可以有以下神明:
如果设置 required=“false”,你也可以在运行时使用 PackageManager.hasSystemFeature() 确定 BLE 的可用性:
关于蓝牙的开启,请参考 Android 蓝牙开发(一) – 传统蓝牙聊天室
要查找 BLE 设备,在 5.0 之前,使用 startLeScan() 方法,它会返回当前设备和外设的广播数据。不过在 5.0 之后,使用 startScan() 去扫描,这里为了方便手机充当外围设备,统一使用 5.0 之后的方法。
而扫描是耗时的,我们应该在扫描到想要的设备后就立即停止或者在规定时间内停止,扫描代码如下:
可以看到,在 5.0 之后可以通过 ScanSettings 进行扫描的一些设备,比如设置扫描模式 setScanMode ,在 startScan() 中,也可以过滤自己的 UUID,从而省去一些时间。接着在扫描回调中,把能获取名字的设备通过回调给 recyclerview 去回调。
效果如下:
上面说到,Android 5.0 之后,手机也能充当外围设备,这里我们也来实践一下;
首先,Android要完成一个外围设备,需要完成以下步骤:
- 编写广播设置,比如发送实践,发送功率等
- 编写广播数据,这个是需要的,需要设置 service 的uuid,或者显示名字等
- 编写扫描广播(可选),这个广播当中心设备扫描时,数据能被接受的广播,通常我们会在这里编写一些厂商数据
- 添加 Gatt service ,用来跟中心设备通信
在发送广播之前,我们可以先对广播进行一些配置:
可以看到,这里设置成可连接广播,且广播模式设置为 SCAN_MODE_LOW_LATENCY 高功耗模式 ,它共有三种模式:
- SCAN_MODE_LOW_POWER : 低功耗模式,默认此模式,如果应用不在前台,则强制此模式
- SCAN_MODE_BALANCED : 平衡模式,一定频率下返回结果
- SCAN_MODE_LOW_LATENCY 高功耗模式,建议应用在前台才使用此模式
发送功率也是可选的:
- 使用高TX功率级别进行广播:AdvertiseSettings#ADVERTISE_TX_POWER_HIGH
- 使用低TX功率级别进行广播:AdvertiseSettings#ADVERTISE_TX_POWER_LOW
- 使用中等TX功率级别进行广播:AdvertiseSettings#ADVERTISE_TX_POWER_MEDIUM
- 使用最低传输(TX)功率级别进行广播:AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW
接着,是广播数据包:
比较好理解,让广播显示手机蓝牙名字,并设置服务的 UUID
扫描广播是当中心设备在扫描时,能够显示出来的广播,它可以添加一些必要数据,如厂商数据,服务数据等,注意!与上面的广播一样,不能超过31个字节。
最后,使用 startAdvertising() 就可以开始发送广播了:
使用 去监听广播开启成功与否:
此时,你去搜索,就能搜到你手机的蓝牙名称和对应的广播数据了。
但如果外围设备想要与中心设备通信,还需要启动 Gatt service 才行,上面说到,启动Service 时,我们需要配置特征 Characteristic 和 描述符 Descriptor,这里我们来解释以下。
4.3 特征 Characteristic
Characteristic 是Gatt通信最小的逻辑单元,一个 characteristic 包含一个单一 value 变量 和 0-n个用来描述 characteristic 变量的 描述符 Descriptor。与 service 相似,每个 characteristic 用 16bit或者32bit的uuid作为标识,实际的通信中,也是通过 Characteristic 进行读写通信的。
所以为了方便通信,这里我们要添加读写的 Characteristic。
描述符 Descriptor
它的定义就是描述 GattCharacteristic 值已定义的属性,比如指定可读的属性,可接受范围等,比如为写的 特征添加描述符:
接着,把特征添加到服务中,并使用openGattServer() 去打开 Gatt 服务:
代码比较简单,接着就可以使用 gattServiceCallbak 去监听数据成功与读写的数据了:
上面已经配置了服务端的代码,接着,通过扫描到的广播,使用 BluetoothDevice 的 connectGatt() 方法,来连接 GATT 服务:
此时,如果你的配置没有出错的话,就可以通过 BluetoothGattCallback 回调连接到设备了:
代码应该好看懂,就是 onConnectionStateChange() 中的 newState 为 BluetoothProfile.STATE_CONNECTED 时,表示已经连接上了,这个时候,尝试去发现这个服务,如果也能回调 onServicesDiscovered() 方法,则证明此时 GATT 服务已经成功建立,可以进行通信了。
此时就可以来读取外围设备的数据,这个数据是外围设备给中心设备去读的,所以,外围设备的读回调是这样的:
外围设备的BluetoothGattServerCallback
很简单,就是发送一个 "this is a test " 的字符传
中心设备读
如果找得到 GATT 服务,则通过 getCharacteristic() 拿到 GATT 通信的最小单元 Characteristic,通过 mBluetoothGatt?.readCharacteristic(characteristic) 读取数据,这样就会在 BluetoothGattCallback回调的 onCharacteristicRead 拿到数据:
同理写也一样,这样我们的 BLE 低功耗蓝牙就学习结束了