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个额外的必需参数:withServicesprescanDuration。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实现示例:链接

GitHub

https://github.com/PhilipsHue/flutter_reactive_ble