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

查看:76
本文介绍了带有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.

推荐答案

由于您要求蓝牙连接必须在后台继续工作,因此您应该使用

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交互的单例类(从现在开始称为Manager).由于某些BLE操作需要Android上下文,因此一种好方法是将Application上下文用作上下文.遵循在Android上获取上下文"的静态方法吗?能够随时获取该上下文或将Application类作为其子类,并从其onCreate中调用Manager中的某些初始化方法并传递该上下文.现在,您可以将所有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.

要实现扫描功能,您可以在Manager中拥有一个创建Scanner对象的方法.将Scanner类编写为Android BLE扫描器的包装,并公开用于启动/停止扫描的方法.当您创建扫描程序时,该方法还应该将接口用作用于回调(设备报告和错误)的参数.该类现在可以在例如Activity中使用.只需确保扫描仪已停止在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创建设备对象,这些对象具有启动/停止连接的方法以及具有报告事件的回调接口.对于每个受支持的功能(例如,读取一些远程值),您都应该公开一个方法来启动请求并具有一个回调,当结果到达时将调用该回调.然后,您的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内容,例如当蓝牙关闭时自动在您的活动中显示警告消息,而在蓝牙打开时将其删除,则您应该能够将监听器注册到Manager.在Manager中有一个用于注册/注销注册监听器(实际上只是一个回调)对象的方法,跟踪所有监听器,并在蓝牙状态发生变化时调用所有监听器.然后在活动"的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.如果您在应用程序中不使用主线程以外的任何东西,则可以例如将Manager中所有的回调调用都发布到主线程中,或者可以在来自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代码以使用户授予Location权限以能够开始扫描.我建议您在UI代码中而不是在Manager中实现此目的,或者在Manager中实现一些包装器"或帮助器方法.

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天全站免登陆