Flutter 响应式 BLE 库
处理多个设备的BLE操作的Flutter库。
用法
响应式 BLE 库支持以下功能
- BLE 设备发现
- 观察主机设备 BLE 状态
- 建立 BLE 连接
- 维护多个 BLE 设备的连接状态
- 发现服务(将是隐式的)
- 读取 / 写入特征
- 订阅特征
- 清除 GATT 缓存
- 协商 MTU 大小
初始化
应通过以下方式初始化库
final flutterReactiveBle = FlutterReactiveBle();
设备发现
应按此方式发现 BLE 设备
flutterReactiveBle.scanForDevices(withServices: [serviceId], scanMode: ScanMode.lowLatency).listen((device) {
//code for handling results
}, onError: () {
//code for handling error
});
withServices 参数指定要查找的广播服务 ID。如果传入空列表,将报告所有广播设备。scanMode 参数仅在 Android 上使用,并遵循 Android 参考页 ScanSettings 中描述的约定。如果省略 scanMode,将使用均衡扫描模式。
观察主机设备 BLE 状态
使用 statusStream 获取有关主机设备(运行应用程序的设备) BLE 状态的更新。此流可用于确定设备上的 BLE 是否已打开,或者是否已授予必要的权限。示例用法
_ble.statusStream.listen((status) {
//code for handling status update
});
使用 _ble.status 获取主机设备的当前状态。
有关不同状态含义的更多信息,请参阅 BleStatus。
建立连接
要与设备交互,您首先需要建立连接
flutterReactiveBle.connectToDevice(
id: foundDeviceId,
servicesWithCharacteristicsToDiscover: {serviceId: [char1, char2]},
connectionTimeout: const Duration(seconds: 2),
).listen((connectionState) {
// Handle connection state updates
}, onError: (Object error) {
// Handle a possible error
});
对于必需的 id 参数,请使用通过设备发现检索到的设备 ID。在 iOS 上,设备 ID 是 UUID,而在 Android 上,它是 MAC 地址(根据 Android 版本,它也可能是随机化的)。提供一个包含您希望发现的服务和特征 ID 的映射可以加快在 iOS 上的连接速度(否则将发现*所有*服务和特征)。您可以在客户端指定 connectionTimeout,以便在连接在指定时间内无法建立时提供错误。
Android BLE 堆栈存在许多问题,当您尝试连接到不在范围内的设备时,会导致其挂起。为了解决此问题,请使用 connectToAdvertisingDevice 方法先扫描设备,然后仅在找到它时才连接。
flutterReactiveBle.connectToAdvertisingDevice(
id: foundDeviceId,
withServices: [serviceUuid],
prescanDuration: const Duration(seconds: 5),
servicesWithCharacteristicsToDiscover: {serviceId: [char1, char2]},
connectionTimeout: const Duration(seconds: 2),
).listen((connectionState) {
// Handle connection state updates
}, onError: (dynamic error) {
// Handle a possible error
});
除了上面描述的正常连接参数外,此函数还具有 2 个额外的必需参数:withServices 和 prescanDuration。PrescanDuration 是在尝试连接之前 BLE 堆栈将扫描设备的时间(如果找到设备)。
读/写特征
读取特征
final characteristic = QualifiedCharacteristic(serviceId: serviceUuid, characteristicId: characteristicUuid, deviceId: foundDeviceId);
final response = await flutterReactiveBle.readCharacteristic(characteristic);
写响应
将值写入特征并等待响应。“写入特征响应”中的“响应”是指“接收确认”。写入可以被确认(成功)或失败(抛出异常),因此返回类型为 void,无需打印(但您可以 print("Write successful"),并在 catch 子句中 print("Write failed: $e"))。
BLE 本身并未提供您可能从 HTTP 了解到的请求-响应机制。如果您需要执行请求-响应调用,则需要在基本 BLE 功能之上实现自定义机制。典型方法是实现一个“控制点”:一个可写入的特征,该特征会传递 通知或指示,以便将请求写入其中,并将响应作为通知或指示传回。
final characteristic = QualifiedCharacteristic(serviceId: serviceUuid, characteristicId: characteristicUuid, deviceId: foundDeviceId);
await flutterReactiveBle.writeCharacteristicWithResponse(characteristic, value: [0x00]);
无响应写入
如果您想在短时间内执行多个连续写入操作(例如,将固件上传到设备)或者设备不提供响应,请使用此操作。从性能上看,这是写入值的最快方式,但 BLE 设备可能会无法处理连续的写入,因此请偶尔进行一次 writeWithResponse。
final characteristic = QualifiedCharacteristic(serviceId: serviceUuid, characteristicId: characteristicUuid, deviceId: foundDeviceId);
flutterReactiveBle.writeCharacteristicWithoutResponse(characteristic, value: [0x00]);
订阅特征
您也可以监听通知(如果特定服务支持),而不是定期读取特征,以便在值发生更改时收到通知。
final characteristic = QualifiedCharacteristic(serviceId: serviceUuid, characteristicId: characteristicUuid, deviceId: foundDeviceId);
flutterReactiveBle.subscribeToCharacteristic(characteristic).listen((data) {
// code to handle incoming data
}, onError: (dynamic error) {
// code to handle errors
});
协商 MTU 大小
您可以增加或减小 MTU 大小以达到更高的吞吐量。此操作将返回实际协商的 MTU 大小,但不能保证请求的大小能够成功协商。iOS 有一个默认的 MTU 大小,无法协商,但是您仍然可以使用此操作来获取当前的 MTU。
final mtu = await flutterReactiveBle.requestMtu(deviceId: foundDeviceId, mtu: 250);
Android 特有操作
以下操作仅对 Android 有效,iOS 不支持。在 iOS 上使用这些操作时,库将抛出 UnSupportedOperationException。
请求连接优先级
在 Android 上,您可以向 BLE 设备发送连接优先级更新。priority 参数是一个枚举,它使用与 BluetoothGatt Android 规范 相同的规范。使用 highPerformance 会增加电池消耗,但会加快 GATT 操作。与多个设备通信时,请谨慎设置优先级,因为如果您为所有设备设置了 highperformance,增加优先级的效果会降低。
await flutterReactiveBle.requestConnectionPriority(deviceId: foundDeviceId, priority: ConnectionPriority.highPerformance);
清除 GATT 缓存
Android OS 会为每个设备维护一个已发现服务缓存的表。有时,在固件更新后引入了新服务,但缓存未更新。要使缓存无效,您可以使用 cleargattCache 操作。
这是一个隐藏的 BLE 操作,应极其谨慎地使用,因为它在 允许列表 上。
await flutterReactiveBle.clearGattCache(foundDeviceId);
贡献
欢迎提交新的 issue 或 pull request,以改进此项目
设置
此项目使用 melos 来管理此仓库中的所有软件包。
安装 melos:dart pub global activate melos 设置 melos 指向您本地文件夹中的依赖项:melos bootstrap
Android
库要求 Kotlin 版本为 1.5.31。
更新 Kotlin 版本
要更新 Kotlin 版本,请打开 Android Studio,然后转到 Tools > Kotlin > Configure Kotlin plugin updates,并将 Update channel 更新为 1.5.x。
常见问题
如何处理 BLE 无法传递异常
在 Android 端,我们使用 Polidea 的 RxAndroidBle 库。迁移到 RxJava 2 后,某些错误未能正确路由到其监听器,因此这将导致 BLE 无法传递异常。根本原因在于 Android OS 的线程。作为一种变通方法,RxJava 有一个钩子,您可以在其中设置全局错误处理程序。有关更多信息,请参阅 RxJava 文档。
Flutter 应用程序中的默认变通方法实现(需要在 Java / Kotlin 部分,例如 MainActivity)如下所示。以 Java 为例,请参阅 Polidea RxAndroidBle 示例。
BleException 来自 Polidea RxAndroidBle,因此请确保您的应用程序声明了以下依赖项:implementation "com.polidea.rxandroidble2:rxandroidble:1.11.1"
RxJavaPlugins.setErrorHandler { throwable ->
if (throwable is UndeliverableException && throwable.cause is BleException) {
return@setErrorHandler // ignore BleExceptions since we do not have subscriber
}
else {
throw throwable
}
}
需要哪些权限?
Android
对于 Android,该库使用以下权限,具体取决于 SDK 级别
最高 SDK 30 (Android 11)
- ACCESS_FINE_LOCATION:此权限是必需的,因为旧的 Nexus 设备需要位置服务才能提供可靠的扫描结果
- BLUETOOTH:允许应用程序连接到已配对的蓝牙设备
- BLUETOOTH_ADMIN:允许应用程序发现和配对蓝牙设备
- BLUETOOTH_SCAN:添加此权限并设置
tools:node="remove"以从合并的 manifest 中删除它。
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" tools:node="remove" />
这将防止出现 #410 等问题。
SDK 31 及更高版本 (Android 12+)
- BLUETOOTH_CONNECT:允许应用程序连接到蓝牙设备
- BLUETOOTH_SCAN:允许应用程序扫描蓝牙设备
这些权限已包含在此库的 manifest 中,因此应自动合并到您应用程序的 manifest 中。无需在 manifest 中添加这些权限。
仅当使用 Android SDK 31 (Android 12) 及更高版本时,请根据您应用程序的用例,确保您的 manifest 已正确设置为 android:name="android.permission.BLUETOOTH_SCAN" 权限的 android:usesPermissionFlags="neverForLocation"。有关更多信息,请参阅 此链接,以及 示例应用程序的 manifest 以获取示例用法。
iOS
对于 iOS,您需要在应用程序的 Info.plist 文件中添加以下条目。未经此项,不允许访问 Core Bluetooth。有关如何实现此目的,请参阅 我们的示例应用程序。有关更深入的详细信息:关于 iOS 蓝牙权限的博客文章
iOS13 及更高版本
- NSBluetoothAlwaysUsageDescription
iOS12 及更低版本
- NSBluetoothPeripheralUsageDescription
如何调整 ProGuard (Android)
如果您正在使用 ProGuard,请将以下代码片段添加到您的 proguard-rules.pro 文件中
-keep class com.signify.hue.** { *; }
这将防止出现 #131 等问题。
为什么 BLE 堆栈不直接连接到我的外围设备
在您能够执行 BLE 操作之前,设备的 BLE 堆栈会确保所有内容都已正确设置,然后报告已准备就绪。对于某些设备,这比其他设备花费的时间更长。启动应用程序时,请确保在执行 BLE 操作之前 BLE 堆栈已正确初始化。最安全的方法是监听 statusStream 并等待 BleStatus.ready。
这将防止出现 #147 等问题。
非官方示例应用程序
示例实现 UART over BLE:链接