与BLE的Android应用程序架构

Sam*_*Sam 2 architecture android bluetooth-lowenergy

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

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

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

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

Emi*_*mil 10

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

是否实际将BLE代码放在此服务类中与功能无关.

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

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

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

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

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

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

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

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

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