项目需要接入两个低功耗蓝牙设备 (BLE), 并且与之交互(读 / 写) 数据, 所以看了下官方对于这块儿的介绍, 总结了一下 BLE 开发中一些需要注意的地方以及基本流程.
BLE 开发需要 Android 4.3 (API level 18) 及以上
一. 添加权限 为了能正常使用蓝牙相关功能(扫描等), 首先需要添加以下权限:
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
复制代码
在 Android6.0 及以上系统中, 我们需要动态申请权限, 这里推荐使用 RxPermissions
简单介绍下 RxPermissions 如何引入.
1. 在根 build 文件中添加代码:
- ...
- allprojects {
- repositories {
- maven { url 'https://jitpack.io' }
- }
- }
- ...
复制代码
2. 在对应 moudle 的 build 文件中添加依赖:
- ...
- dependencies {
- implementation 'com.github.tbruyelle:rxpermissions:0.10.2'
- }
- ...
复制代码
3. 使用:
- RxPermissions rxPermissions=new RxPermissions(this);
- rxPermissions.request(Manifest.permission.BLUETOOTH,
- Manifest.permission.BLUETOOTH_ADMIN,
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION)
- .subscribe(granted -> {
- if (granted) {
- // 权限允许成功
- }
- });
复制代码
如果想了解 RxPermissions 更多用法, 戳这里 https://github.com/tbruyelle/RxPermissions .
二. 判断设备是否支持蓝牙 这里有两种处理方式:
如果你想让只有支持 BLE 的手机才能安装你的应用程序的话, 可以在清单文件中添加如下内容, 这样的话如果设备不支持 BLE 的话你的应用都装不上, 当然这种方式不太友好:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
复制代码
在代码中判断当前设备是否支持 BLE, 以对用户做出反馈. 首先, 在清单文件中声明需要使用 BLE 特性, 不过 required 这里设置为 false, 然后在 app 运行时通过
PackageManager.hasSystemFeature()
来判断设备是否支持 ble:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
复制代码
- // Use this check to determine whether BLE is supported on the device. Then
- // you can selectively disable BLE-related features.
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
- Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
- finish();
- }
复制代码
三. 扫描蓝牙设备 BLE 设备的扫描由 BluetoothManager 对象提供方法来实现, 有两个扫描方法:
- public boolean startLeScan(BluetoothAdapter.LeScanCallback callback) {
- throw new RuntimeException("Stub!");
- }
- public boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback) {
- throw new RuntimeException("Stub!");
- }
复制代码
第二个方法允许我们提供特定的 UUID, 来扫描特定的设备, 扫描结果通过 BluetoothAdapter.LeScanCallback 接口回调给我们:
- public interface LeScanCallback {
- /**
- * Callback reporting an LE device found during a device scan initiated
- * by the {@link BluetoothAdapter#startLeScan} function.
- *
- * @param device Identifies the remote device
- * @param rssi The RSSI value for the remote device as reported by the
- * Bluetooth hardware. 0 if no RSSI value is available.
- * @param scanRecord The content of the advertisement record offered by
- * the remote device.
- */
- public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
- }
复制代码
四. 获取远程 BLE 设备 在扫描出设备以后, 我们一般会选择某个扫描出来的设备, 通过其地址获取一个远程的蓝牙设备对象.
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address)
复制代码
五. 连接 BLE 设备的 GATT 服务 与 BLE 设备交互的第一步是连接到它, 更具体地说, 连接到设备上的 GATT 服务. 要在 BLE 设备上连接到 GATT 服务, 可以使用 connectGatt()方法. 该方法接受三个参数: 一个上下文对象, autoConnect(布尔值表示是否在 BLE 设备可用时自动连接到该设备), 以及对 BluetoothGattCallback 的引用:
mBluetoothGatt = device.connectGatt(context, true, mGattCallback);
复制代码
以上代码可以连接到由 BLE 设备托管的 GATT 服务, 并返回一个 BluetoothGatt 实例, 然后可以使用它来执行 GATT 客户端操作, 例如写数据等. 呼叫者 (Android 应用程序) 是 GATT 客户端. 连接状态, 以及 GATT 的数据变化等通过 BluetoothGattCallback 接口回调给客户端(APP).
一般使用 BluetoothGattCallback 的这些回调方法:
1. 获取连接状态, 在连接成功时扫描设备服务
- @Override
- public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
- if (newState == BluetoothProfile.STATE_CONNECTED) {
- if (connectChangedListener != null) {
- connectChangedListener.onConnected();
- }
- mConnectionState = STATE_CONNECTED;
- mBluetoothGatt.discoverServices();
- } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
- if (connectChangedListener != null) {
- connectChangedListener.onDisconnected();
- }
- mConnectionState = STATE_DISCONNECTED;
- }
- }
复制代码
2. 获取服务, 特性等 一个 BLE 设备可能有多个服务 BluetoothGattService, 同样每个服务可以有多个 BluetoothGattCharacteristic 特性.
- @Override
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
- if (status == BluetoothGatt.GATT_SUCCESS) {
- List<BluetoothGattService> services = mBluetoothGatt.getServices();
- for (int i = 0; i <services.size(); i++) {
- HashMap<String, BluetoothGattCharacteristic> charMap = new HashMap<>();
- BluetoothGattService bluetoothGattService = services.get(i);
- String serviceUuid = bluetoothGattService.getUuid().toString();
- List<BluetoothGattCharacteristic> characteristics = bluetoothGattService.getCharacteristics();
- for (int j = 0; j < characteristics.size(); j++) {
- charMap.put(characteristics.get(j).getUuid().toString(), characteristics.get(j));
- }
- servicesMap.put(serviceUuid, charMap);
- }
- BluetoothGattCharacteristic bluetoothGattCharacteristic = getBluetoothGattCharacteristic(UUID_SERVICE, UUID_CHARACTERISTIC);
- if (bluetoothGattCharacteristic == null)
- return;
- enableGattServicesNotification(bluetoothGattCharacteristic);
- } else {
- Log.w(TAG, "--------- onServicesDiscovered received:" + status);
- }
- }
复制代码
在上面的代码中, 我们将 BLE 设备的所有 BluetoothGattService 和 BluetoothGattCharacteristic 全部保存下来, 但是在实际需求中, 我们一般只会与某个特定 BluetoothGattService 中的某个特性 BluetoothGattCharacteristic 进行数据读写. 判断条件就是这里的 UUID_SERVICE 和 UUID_CHARACTERISTIC, 这两个 UUID 一般提供 BLE 设备的时候会一并提供给我们.
找到这个特定的 BluetoothGattCharacteristic 后, 我们希望它发生改变时可以得到通知, 可以使用 setCharacteristicNotification()方法为特性设置通知:
- BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(UUID_DESCRIPTOR));
- descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
- mBluetoothGatt.writeDescriptor(descriptor);
复制代码
3. 监听数据变化 经过以上设置, 我们就可以在 onCharacteristicChanged 回调方法中获取 BLE 设备发过来的数据了:
- @Override
- public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
- // 解析数据
- parseData(characteristic);
- }
复制代码
当然, 我们也可以用第五步中获取的 mBluetoothGatt 来向 BLE 设备发送数据:
- mBleGattCharacteristic.setValue(HexUtil.hexStringToBytes(value));
- boolean b=mBluetoothGatt.writeCharacteristic(mBleGattCharacteristic);
复制代码
以上, 就是 Android 端与 BLE 设备通信的基本开发流程, 这里我抽成了一个 Demo, 项目目录如下:
几点说明:
因为我这里需求是接入两个 BLE 设备, 所以我抽取了一个 BluetoothLeDeviceBase, 代表基类设备, 将一些通用的属性和操作封装在了这里
BluetoothLeDeviceA,BluetoothLeDeviceB 代表具体的某个 BLE 设备, 每个设备可能有不同之处, 例如数据解析方式等.
完整代码地址: https://github.com/SolveBugs/BlogPracticeDems 目前只是基本的封装, 后续会继续完善.
选择 bluetoothbledemo 这个 moudle 运行即可, 界面如下:
来源: https://juejin.im/post/5b9a3e1fe51d450e72108755