当前位置: 首页 > 知识库问答 >
问题:

BoundService LiveData ViewModel在新Android推荐架构中的最佳实践

壤驷彦
2023-03-14

我一直在苦苦思考,在新的Android推荐架构中,Android服务应该放在哪里。我想出了许多可能的解决方案,但我无法决定哪一个是最好的方法。

我做了很多研究,但我找不到任何有用的指南或教程。关于在我的应用架构中放置服务的唯一提示是这个,来自@JoseAlcerreca Media的帖子

理想情况下,ViewModels不应该了解Android。这提高了可测试性、泄漏安全性和模块化。一般的经验法则是确保没有android。*在ViewModels中导入(android.arch.*等例外)。这同样适用于演讲者。

因此,我应该将我的Android服务放在架构组件层次结构的顶部,与我的活动和片段处于同一级别。这是因为Android服务是Android框架的一部分,所以ViewModels不应该知道它们。

现在,我将简要解释我的场景,但只是为了使全景更清晰,而不是因为我想要这个特定场景的答案。

  • 我有一个Android应用程序,它有一个MainActivity,其中包含许多片段,所有片段都绑定在一个底部导航栏中
  • 我有一个绑定到myActivity及其一个片段的BluetoothService(因为我希望该服务具有与Activty相同的生命周期,但我也希望直接从片段与之交互)
  • 片段与BluetoothService交互以获取两种类型的信息:
    • 有关蓝牙连接状态的信息。不需要坚持
    • 来自蓝牙设备的数据(这是一个秤,所以这里指的是体重和身体组成)。需要坚持

    以下是我能想到的三种不同的体系结构:

    AndroidService中的LiveData

    更新:这是我当时个人采用的方法,因为它工作得很好,让我可以相对较快地完成。然而,我建议遵循Jeel Vankhede的更新答案,这似乎是一个更“惯用”的实现。

    • 具有连接状态和来自蓝牙设备的重量测量的LiveData在BluetoothService内部。
    • 片段可以触发BluetoothService(例如scanDevices)中的操作
    • 片段观察有关连接状态的LiveData,并相应地调整UI(例如,如果状态已连接,则启用按钮)。
    • 片段观察新的重量测量的LiveData。如果新的重量测量来自BluetoothDevice,片段会告诉自己的ViewModel来保存新的数据。这是通过Repository类完成的。

    • 该片段可以触发BluetoothService中的操作(例如scanDevices)
    • Bluetooth服务更新共享ViewModel中与蓝牙相关的实时数据
    • 片段在自己的ViewModel中观察LiveData

    • 片段可以触发BluetoothService(例如scanDevices)中的操作
    • BluetoothService在自己的ViewModel中更新蓝牙相关的LiveData。
    • 片段在自己的ViewModel和BluetoothService ViewModel中观察LiveData。

    我很确定我应该把它们放在架构的顶部,把它们当作一个活动/片段来对待,因为BoundServices是Android框架的一部分,它们由Android操作系统管理,并与其他活动和片段绑定。在这种情况下,我不知道与LiveData、ViewModels和活动/片段交互的最佳方式是什么。

    有些人可能认为它们应该被视为一个数据源(因为在我的例子中,它是使用蓝牙从秤上获取数据),但我认为这不是一个好主意,因为我在上一段中已经说过,特别是因为它在这里说:

    避免将应用程序的入口点(如活动、服务和广播接收器)指定为数据源。相反,它们应该只与其他组件协调,以检索与该入口点相关的数据子集。每个应用程序组件的寿命都很短,这取决于用户与设备的交互以及系统当前的整体运行状况。

    所以,最后,我的问题是:

    我们应该把Android(绑定)服务放在哪里,它们与其他架构组件有什么关系?这些替代方案中有哪一个是好方法吗?

共有3个答案

东门奕
2023-03-14

如果我们绑定/解除绑定到/从服务的活动或多个活动像往常一样在onstart/onStation,然后我们有单例实例,持有蓝牙相关管理器(我使用北欧lib为ble管理器)。这个实例在服务中,这样我们就可以断开连接,例如当服务被破坏时,因为用户界面与它解除了绑定,当服务被创建时,我们可以重新连接到ble。我们还将ble管理器单例注入到视图模型中,通过livedata或rx或ble管理器提供的类似的反应性数据,例如连接状态,使交互和数据监听变得更加容易。通过这种方式,我们可以从视图模型与ble、订阅特性等进行交互,服务提供了可以在多个活动中生存的范围,并且基本上知道何时连接或断开连接。我已经在我的应用程序中尝试过这种方法,到目前为止它还可以。

示例项目https://github.com/uberchilly/BoundServiceMVVM

詹斌蔚
2023-03-14

在仍然能够使用Android服务的情况下,避免直接接触该服务的一种方法是通过接口对象。这是首字母缩略词SOLID中接口隔离“I”的一部分。下面是一个小例子:

public interface MyFriendlyInterface {
    public boolean cleanMethodToAchieveBusinessFunctionality();
    public boolean anotherCleanMethod();
}

public class MyInterfaceObject implements MyFriendlyInterface {
    public boolean cleanMethodToAchieveBusinessFunctionality() {
        BluetoothObject obj = android.Bluetooth.nastySubroutine();
        android.Bluetooth.nastySubroutineTwo(obj);
    }

    public boolean anotherCleanMethod() {
        android.Bluetooth.anotherMethodYourPresentersAndViewModelsShouldntSee();
    }
}

public class MyViewModel {
    private MyFriendlyInterface _myInterfaceObject;

    public MyViewModel() {
        _myInterfaceObject = new MyInterfaceObject();
        _myInterfaceObject.cleanMethodToAchieveBusinessFunctionality();
    }
}

鉴于上述范例,您可以自由地将服务放在包含POJO代码的包之外的包中。没有“正确”的位置来放置您的服务——但放置它们的位置肯定是错误的(例如,POJO代码所在的位置)。

西门靖琪
2023-03-14

在得到@Ibrahim Disouki(谢谢)的建议后,我深入挖掘,发现了一些有趣的东西!以下是背景。

O. P.寻求解决方案“考虑Android架构组件,Android框架的服务组件在哪里”。所以,这里是开箱即用的(SDK)解决方案。

它与活动/片段处于同一水平。怎样如果你要扩展服务类,那么就开始扩展LifecycleService。这背后的原因很简单,以前我们必须依靠活动/片段生命周期来接收更新/对服务执行一些上下文操作。但现在情况并非如此。

现在,它有自己的生命周期注册表/维护者,称为ServiceLiefycleDispatcher,它负责服务的生命周期,这也使它成为生命周期所有者。

它留给我们的条件是,从现在开始,你可以有一个ViewModelLifececleService为自己做操作

在O.P.的上下文中,LifecycleService现在可以维护它的ViewModel执行与存储库层相关的业务逻辑,之后在另一个生命周期感知组件上,如Activity/Fragment,也可以使用/重用相同的ViewModel,对其进行特定操作。

请注意,通过这样做,您将处于两个不同的生命周期所有者的状态(活动

(我建议不要通读)

在我看来,服务应该和活动/片段处于同一级别,因为它是框架组件

所以,这里的困境是,有时(在您的情况下),服务充当数据源,从一些长期运行的任务向UI提供数据。

那么Android架构组件中应该包含什么呢?我认为你可以把它当作生命周期观察者。因为,不管你在后台做什么,你都需要考虑LifecycleOwner的生命周期。

为什么?因为,我们通常会将其绑定到LifecycleOwner(活动/片段)

如何实现它?

>

  • 使用您的服务类并实现LifecycleObserver接口。

    当您将您的服务绑定到活动/片段时,在您的服务类的服务连接期间,通过调用方法将您的服务添加到您的活动中,作为生命观察者。

    现在,在服务类中使用一个接口来提供从服务到用户界面的回调,每次数据更改时,检查您的服务是否至少有生命周期事件创建或恢复来提供回调。

    这样,我们就不需要LiveData从服务更新到,甚至不需要ViewModel(为什么我们需要它来服务?我们不需要配置更改来在服务生命周期中生存。VM的主要任务是在生命周期之间包含数据)。

    边注:如果你认为你有长时间运行后台操作,那么考虑使用<代码>工作管理器< /代码>。使用此库后,您会觉得现在应该将服务标记为已弃用!(只是一个随机的想法)

  •  类似资料:
    • 本文向大家介绍推荐JavaScript实现继承的最佳方式,包括了推荐JavaScript实现继承的最佳方式的使用技巧和注意事项,需要的朋友参考一下    实现JavaScript继承的最简单的方式是call方法(或者apply方法)及原型链方法,但这两种方法都有缺陷,而其混合体就是很好的继承实现方式。下面举例说明:     对于类Animal来说,它有一个字段属性age及函数属性sayAge,sa

    • 本文向大家介绍IntelliJ IDEA最佳配置(推荐),包括了IntelliJ IDEA最佳配置(推荐)的使用技巧和注意事项,需要的朋友参考一下 IntelliJ IDEA最佳配置 IntelliJ IDEA 分为两个版本:旗舰版(Ultimate)和社区版(Community)。旗舰版收费(30天免费使用时间,功能齐全);社区版(永久免费,功能简陋)。 1.安装目录结构解释 bin:容器,执行

    • 本文向大家介绍2014年最新推荐的10款 PHP 开发框架,包括了2014年最新推荐的10款 PHP 开发框架的使用技巧和注意事项,需要的朋友参考一下 PHP去年发生了翻天覆地的变化。似乎每个人对于一个好的框架应该是什么样子都有自己的想法,但话又说回来,不同的框架适用于不同类型的项目,没有什么框架是万能的。 优秀的 PHP 框架可以帮助开发者构建干净整洁和结构化的 Web 开发,同时也加快了创建和

    • 我试图使用Kotlin实现干净的架构。这一过程的流程将是: 代码示例:

    • Warning: This information refers to third-party sites, products, or modules that are not maintained by the Expressjs team. Listing here does not constitute an endorsement or recommendation from the Ex