使用BLE

时间:2017-12-31 01:13:10

标签: android architecture bluetooth-lowenergy

我正在使用Android开发一个带有BLE API的Android应用程序。我的应用程序需要连接到BLE设备,并且只要它在范围内并打开就会保持连接状态。我需要从中读取数据,并将数据写入其中。

我正在尝试遵循MVP架构模式,并不严格,因为活动是起点。但无论如何,我想知道我应该在哪里与蓝牙进行交互?我正在寻找以下问题的答案。我搜索过StackOverflow,但无法找到我要找的东西。

  1. 是否应该像在googlesample ble app中那样在与UI绑定的服务中?但是,我认为这将破坏整个mvp架构。
  2. 它应该是一个有限的服务吗?如果不是,那么实施该服务的最佳方式是什么?在我看来,如果它不受视图限制,并且后台服务有回调在UI上显示某些内容,则可能存在未定义的行为。
  3. 谁应该发起蓝牙互动?应用程序类还是一些活动?
  4. 我正在寻找主要的架构指导,以及开发此应用程序的最佳方式。

2 个答案:

答案 0 :(得分:8)

由于您要求蓝牙连接应在后台继续运行,因此您应该在应用进程中的某处运行Foreground Service。这将确保您的应用程序进程保持活跃状态​​,但需要在手机/平板电脑的顶栏中显示一个图标。

您是否真正将BLE代码放入此服务类中并不重要。

当然,有许多方法可以实现良好的架构,但这是我的方法。

我的方法是拥有一个处理所有BLE扫描,连接和GATT交互的单例类(从现在开始称为Manager)。由于某些BLE操作需要Android上下文,因此一种好方法是使用Application上下文作为上下文。可以跟随Static way to get 'Context' on Android?以便能够随时获取该上下文,或者继承Application类,并从其onCreate调用Manager中的一些初始化方法并传递上下文。现在,您可以将所有BLE功能与Android服务/活动/应用程序内容完全分开。只要你把所有东西放在同一个过程中,我就不会真正看到使用有界服务等的重点。

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

包含自定义扫描程序对象的原因有多种,而不是直接在活动中使用Android的BLE扫描API。首先,您可以对广告数据包应用适当的过滤和处理,以便处理您的外围设备类型,并在您的自定义广告报告回调中显示高级参数(从广告数据中解码)。当蓝牙启动/停止/重新启动时,管理员还应该收听广播,并跟踪所有已启动的扫描仪,以便在蓝牙重新启动时(如果您需要此功能)无缝重启扫描仪。您可能还想跟踪所有扫描开始/停止的时间戳,以便您可以解决Nougat中的新限制,将其限制为每30秒扫描5次。

如果要连接外围设备,请使用类似的方法。例如,您可以让Manager创建Device对象,这些对象具有启动/停止连接的方法,并具有报告事件的回调接口。对于每个支持的功能(例如,读取一些远程值),您应该公开一个方法,该方法启动请求并具有在结果到达时调用的回调。然后你的经理和设备类负责处理GATT的东西(包括将所有的GATT请求排入队列,这样你一次只有一个未完成的GATT操作)。只需确保在您不想要结果时总是可以中止或忽略结果,例如,如果调用了活动的onStoponDestroy方法。

由于您可能希望在设备断开连接时自动重新连接,因此您应该使用autoConnect标志并在建立连接时将其设置为true,从而确保这一点。同样,Manager应该跟踪所有活动的Device对象,并在重新启动蓝牙时自动重新创建BluetoothGatt对象。

为了能够显示不同类型的UI内容,例如在蓝牙关闭时自动在您的活动中显示警告消息并在蓝牙打开时将其删除,您应该能够将管理员注册到您的管理器。在Manager中有一个方法来注册/取消注册一个监听器(实际上只是一个Callback)对象,跟踪所有监听器,当蓝牙状态发生变化时,调用所有监听器。然后在您的活动onStart中注册一个监听器,并在onStop中取消注册。您可以在适用的情况下为设备的BLE通知采用类似的方法。

剩下的是你如何处理不同的主题。您可能知道,大多数来自Android API的BLE回调都发生在Binder线程上,因此您可能无法从它们更新UI。如果您在应用程序中没有使用除主线程以外的任何其他内容,您可以将管理器中所有回调的调用发布到主线程,或者可以在Android回调时直接移动到主线程# 39; s BLE堆栈到达(但随后要注意像https://bugs.chromium.org/p/chromium/issues/detail?id=647673这样的东西)。只要确保你从不接触来自不同线程的相同变量。

此外,如果您将API 23或更高版本作为目标,则需要使用UI代码让用户授予位置以便能够开始扫描。我建议你在你的UI代码中而不是在管理器中实现它,或者实现一些"包装器"或管理器中的辅助方法执行此操作。

答案 1 :(得分:0)

RxCentralBle提供了用于应用程序的范例。图书馆的设计清楚地显示了图书馆的结构。简而言之,RxCentralBle为主要的蓝牙LE动作提供了反应性接口:

enter image description here

  • BluetoothDetector-检测手机的蓝牙状态
  • 扫描仪-扫描外围设备
  • ConnectionManager-连接到外围设备
  • PeripheralManager-将与外围设备进行通信的操作排队

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

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