带有 BLE 的 Android 应用架构 [英] Android app architecture with BLE

查看:30
本文介绍了带有 BLE 的 Android 应用架构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am developing an android app with BLE API from android. My app needs to connect to a BLE device, and remain connected as long as it is in range and turned on. I need to read data from it, and write data to it.

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

I am trying to follow the MVP architecture pattern, not strictly since activities are the starting point. But anyway, I wanted to know where should I put the interaction with Bluetooth? I am searching for answers for the following questions. I have searched StackOverflow, but couldn't find what I was looking for.

  1. 它是否应该像在 googlesample ble 应用程序 中那样绑定到 UI 中?但是,我认为这会破坏整个 mvp 架构.
  2. 它应该是一个有界服务吗?如果不是,那么实施该服务的最佳方式是什么?在我看来,如果它不绑定到视图,并且有来自后台服务的回调以在 UI 上显示某些内容,则可能会出现未定义的行为.
  3. 谁应该发起蓝牙交互?应用程序类或某些活动?
  1. Should it be in a service bounded to the UI just like in googlesample ble app ? But, I think that would break the whole mvp architecture.
  2. Should it be a bounded service at all ? If no, what would be the best way to implement the service? In my mind, if it's not bounded to the view, and there is a callback from the background service to display something on the UI, there is a possibility of undefined behavior.
  3. Who should initiate the Bluetooth interaction ? The application class or some activity ?

我主要是在寻找架构指导,以及开发此应用的最佳方式.

I am looking for mainly architectural guidance, and best way to go about developing this app.

推荐答案

由于您要求蓝牙连接应在后台保持工作,因此您应该有一个 Foreground Service 在您的应用进程中运行的某个地方.这将确保您的应用进程保持活动状态,但需要在手机/平板电脑的顶部栏中显示一个图标.

Since you have the requirement that the Bluetooth connection should keep working in the background, you should have a Foreground Service somewhere running in your app process. This will make sure your app process will be kept alive, but requires an icon to be displayed in the phone/tablet's top bar.

您是否真的将 BLE 代码放入此服务类中与功能无关.

Whether you actually put your BLE code in this service class or not doesn't matter for the functionality.

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

There are of course many ways to achieve good architecture but here is my approach.

我的方法是使用一个单例类来处理您所有的 BLE 扫描、连接和 GATT 交互(从现在开始称为管理器).由于一些 BLE 操作需要一个 Android Context,一个好的方法是使用 Application 上下文作为上下文.要么遵循 在 Android 上获取上下文"的静态方式?能够随时获取该上下文或子类化 Application 类,并从它的 onCreate 调用管理器中的一些初始化方法并传递上下文.现在,您可以将所有 BLE 功能与 Android 服务/活动/应用程序完全分开.我真的不明白使用有界服务等的意义,只要你把所有东西都保持在同一个过程中.

My approach would be to have a singleton class that handles all your BLE scanning, connections and GATT interactions (from now on called Manager). Since some BLE operations needs an Android Context, a good way is to use the Application context as context. Either follow Static way to get 'Context' on Android? to be able to fetch that context at any time or subclass the Application class and from its onCreate call some initialization method in your Manager and pass the context. Now you can keep all BLE functionality completely separated from Android Service/Activity/Application stuff. I don't really see the point in using bounded services etc. as long as you keep everything in the same process.

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

To implement a scan functionality, you can have a method in your Manager that creates Scanner objects. Write the Scanner class as a wrapper to Android's BLE scanner and expose methods to start/stop scan. When you create a Scanner that method should also take an interface as argument used for callbacks (device reports and errors). This class can now be used in for example an Activity. Just make sure that the scanner gets stopped in the Activity's onStop method to avoid leakage of objects.

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

There are several reasons for having a wrapped custom Scanner object instead of using Android's BLE scan API directly in the Activity. First you can apply the appropriate filtering and processing of advertising packets so it handles your type of peripheral and can show high level parameters (decoded from advertising data) in your custom advertising report callback. The manager should also listen to broadcasts when Bluetooth gets started/stopped/restarted and keep track of all started Scanners so the Scanners are restarted seamlessly when Bluetooth restarts (if you want this functionality). You may also want to keep track of timestamps of all scan starts/stops so you can workaround the new restrictions in Nougat that limits it to 5 scans per 30 seconds.

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

Use a similar approach when you want to connect to your peripherals. You can for example let the Manager create Device objects which have methods to start/stop the connection and have a callback interface to report events. For each supported feature (for example read some remote value) you should expose a method which starts the requests and have a callback which is called when the result arrives. Then your Manager and Device class takes care of the GATT stuff (including enqueuing all your GATT requests so you only have one outstanding GATT operation at a time). Just make sure you can always abort or ignore the result when you don't want the result, for example if an Activity's onStop or onDestroy method is called.

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

Since you probably want to reconnect automatically in case the device gets disconnected, you should use the autoConnect flag and set it to true when establishing the connection, which assures this. Again, the Manager should keep track of all active Device objects and automatically recreate the BluetoothGatt object when Bluetooth is restarted.

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

To be able to display different kind of UI stuff, like for example automatically show a warning message in your Activity when Bluetooth is turned off and remove it when Bluetooth is turned on, you should be able to register Listeners to your Manager. Have a method in your Manager for registering/unregistering a listener (which is really just a Callback) object, keep track of all the listeners and when Bluetooth state change happens, call all listeners. Then in your Activity's onStart you register a listener and in onStop you unregister it. You can have a similar approach for your Device's BLE notifications, where applicable.

剩下的是你如何处理不同的线程.您可能知道来自 Android API 的大多数 BLE 回调发生在 Binder 线程上,因此您可能不会从它们更新 UI.如果您在应用程序中不使用主线程以外的任何其他内容,则可以例如将管理器中的所有回调调用发布到主线程,或者当来自 Android 的 BLE 堆栈的回调到达时直接移动到主线程(但要注意诸如 https://bugs.chromium.org 之类的事情/p/chromium/issues/detail?id=647673).只要确保你永远不会接触来自不同线程的相同变量.

What's left is how you deal with different Threads. As you might know most BLE callbacks from Android's API happen on Binder threads, so you may not update the UI from them. If you otherwise in your app don't use anything other than the main thread, you can for example post all invocations of callbacks in the Manager to the main thread, or maybe move to the main thread directly when the callback from Android's BLE stack arrives (but then be aware of things like https://bugs.chromium.org/p/chromium/issues/detail?id=647673). Just make sure you never touch the same variables from different threads.

此外,如果您的目标是 API 23 或更高版本,您需要 UI 代码来让用户授予位置权限以开始扫描.我建议您在 UI 代码中而不是在管理器中实现这一点,或者在管理器中实现一些包装器"或辅助方法来执行此操作.

Also if you target API 23 or higher you need UI code to let the user give permission to Location to be able to start scan. I suggest you implement this in your UI code and not in the Manager, or implement some "wrapper" or helper method in the Manager to do this.

这篇关于带有 BLE 的 Android 应用架构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆