Android应用程序架构与BLE

3
我正在使用来自Android的BLE API开发Android应用程序。我的应用程序需要连接到BLE设备,并保持连接状态,只要它在范围内且已打开。我需要从中读取数据并向其写入数据。
我尝试遵循MVP架构模式(由于活动是起点,因此不是严格意义上的),但无论如何,我想知道应该将蓝牙交互放在哪里?我正在寻找以下问题的答案。我已经在StackOverflow上搜索过了,但没有找到我想要的内容。
1. 应该像googlesample ble app中那样在UI绑定服务中吗?但是,我认为那会破坏整个MVP架构。 2. 是否应该绑定服务?如果不是,实现服务的最佳方法是什么?在我的想法中,如果它不绑定到视图,并且有来自后台服务的回调以在UI上显示某些内容,则可能存在未定义的行为。 3. 谁应该启动蓝牙交互?应用程序类还是某个活动?
我主要寻求架构指导和开发此应用程序的最佳方法。

您是否需要在应用程序不在前台时保持蓝牙连接运行?或者只有在某些活动正在运行时才需要? - Emil
只有当应用程序在前台时。 - Sam
您会在单个活动中使用BLE还是需要能够在不同的活动之间“移动”? - Emil
我将在多个活动/片段中使用BLE。 - Sam
而且实际需求已经改变,需要在后台保持蓝牙连接。 - Sam
2个回答

18

由于您的要求是在后台保持蓝牙连接,因此您应该在应用程序进程中运行一个前台服务。这将确保您的应用程序进程保持活动状态,但需要在手机/平板电脑的顶部栏中显示图标。

无论您是否将BLE代码放在此服务类中,对于功能都没有问题。

当然,有许多方法可以实现良好的体系结构,但以下是我的方法。

我的方法是拥有一个单例类,处理所有BLE扫描、连接和GATT交互(从现在开始称为Manager)。由于某些BLE操作需要Android上下文,一个好的方法是使用Application上下文作为上下文。要么按照Static way to get 'Context' on Android?获取任何时间的上下文,要么子类化Application类,并从其onCreate调用一些初始化方法在您的Manager中传递上下文。现在,您可以将所有BLE功能完全与Android Service/Activity/Application分离。只要您将所有内容保留在相同进程中,我真的不认为使用绑定服务等有任何意义。

要实现扫描功能,您可以在Manager中编写一个方法,创建Scanner对象。将Scanner类编写为Android的BLE扫描器包装器,并公开用于启动/停止扫描的方法。当您创建一个Scanner时,该方法应该还会使用一个接口作为参数,用于回调(设备报告和错误)。例如,在Activity中可以使用此类。只需确保在Activity的onStop方法中停止扫描器,以避免对象泄漏。

创建包装的自定义扫描仪对象有几个原因,而不是直接在 Activity 中使用 Android 的 BLE 扫描 API。首先,您可以对广告数据应用适当的过滤和处理,以便处理您的外围设备并在自定义广告报告回调中显示高级参数(从广告数据解码)。此外,管理器应该在 Bluetooth 启动/停止/重新启动时监听广播,并跟踪所有启动的扫描仪,以便在 Bluetooth 重新启动时无缝重启扫描仪(如果您需要此功能)。您还可能希望跟踪所有扫描开始/停止的时间戳,以便解决 Nougat 中的新限制,即每 30 秒最多只能进行 5 次扫描。

当您想连接到您的外围设备时,请使用类似的方法。例如,您可以让 Manager 创建具有启动/停止连接方法和回调接口以报告事件的 Device 对象。对于每个受支持的功能(例如读取某个远程值),您应该公开一个启动请求的方法,并具有在结果到达时调用的回调。然后,您的 Manager 和 Device 类会处理 GATT 相关内容(包括将所有 GATT 请求加入队列,因此您一次只有一个未完成的 GATT 操作)。请确保您始终可以放弃或忽略结果,例如如果调用 Activity 的 onStoponDestroy 方法。

由于您可能希望在设备断开连接的情况下自动重新连接,因此应在建立连接时使用 autoConnect 标志,并将其设置为 true,以确保此功能正常工作。同样,Manager 应该跟踪所有活动的 Device 对象,并在 Bluetooth 重新启动时自动重新创建 BluetoothGatt 对象。

为了能够显示不同类型的UI内容(例如在Activity中自动显示警告消息,当蓝牙关闭时,在蓝牙开启时删除它),您应该能够向Manager注册Listeners。 在Manager中有一个方法可以注册/取消注册监听器(实际上只是一个回调)对象,跟踪所有的监听器,并当蓝牙状态发生变化时调用所有的监听器。然后在Activity的onStart中注册监听器,在onStop中取消注册。对于您设备的BLE通知,如果适用,可以采用类似的方法。
剩下的问题是如何处理不同的线程。正如您所知道的,Android API中大多数BLE回调都在Binder线程上发生,因此您可能无法从它们更新UI。如果您的应用程序除了主线程以外没有使用任何其他线程,例如可以将Manager中所有回调的调用都发布到主线程,或者当来自Android的BLE栈的回调到达时,直接移动到主线程(但要注意像这样的问题)。只要确保从不同的线程永远不会触摸相同的变量即可。
此外,如果您的目标API为23或更高版本,则需要UI代码以让用户授予位置权限以便能够开始扫描。我建议您在UI代码中实现此功能,而不是在Manager中实现,或者在Manager中实现一些“包装器”或辅助方法来完成此任务。

like your answer - HeyAlex

0

RxCentralBle 提供了一种在应用程序中使用的范例。该库的设计清晰地展示了库的结构。简而言之,RxCentralBle 为主要的蓝牙 LE 操作提供了反应式接口:

enter image description here

  • BluetoothDetector - 检测手机蓝牙状态
  • Scanner - 扫描外设
  • ConnectionManager - 连接到外设
  • PeripheralManager - 排队操作以与外设通信

建议在后台线程订阅这些接口,并确保资源和订阅在应用程序范围内存在,即您的 Application 类的成员变量。只要您的应用程序正在运行,所有蓝牙 LE 资源都将保持活动状态。

查看 RxCentralBle 的 Wiki 和示例应用程序以了解更多信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接