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("写入成功"),并在catch块中print("写入失败: $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规范相同的规范
与BluetoothGatt Android规范相同的规范。
使用highPerformance会增加电池消耗,但会加快GATT操作。在与多个设备通信时设置优先级时要小心,因为如果您为所有设备设置高优先级,则增加优先级的效果会降低。
await flutterReactiveBle.requestConnectionPriority(deviceId: foundDeviceId, priority: ConnectionPriority.highPerformance);
清除GATT缓存
Android OS会为每个设备维护一个已发现服务的表缓存。有时,在固件更新后引入了新服务,但缓存未更新。要使缓存无效,可以使用cleargattCache操作。
这是一个隐藏的BLE操作,应极其谨慎使用,因为它位于灰名单上。
await flutterReactiveBle.clearGattCache(foundDeviceId);
常见问题
如何处理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,该库使用以下权限
- ACCESS_FINE_LOCATION : 需要此权限是因为旧的Nexus设备需要位置服务才能提供可靠的扫描结果
- BLUETOOTH : 允许应用程序连接到已配对的蓝牙设备
- BLUETOOTH_ADMIN: 允许应用程序发现和配对蓝牙设备
这些权限已包含在此库的清单中,因此应自动合并
到您的应用程序的清单中。无需在您的清单中添加权限。
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 的问题。
非官方示例应用程序
BLE上的UART实现示例:链接