在 Android 上使用 MVVM 时,每个 Activity 是不是应该有一个(且只有一个)ViewModel?

Posted

技术标签:

【中文标题】在 Android 上使用 MVVM 时,每个 Activity 是不是应该有一个(且只有一个)ViewModel?【英文标题】:When using MVVM on Android, should each Activity have one (and only one) ViewModel?在 Android 上使用 MVVM 时,每个 Activity 是否应该有一个(且只有一个)ViewModel? 【发布时间】:2017-11-06 14:25:15 【问题描述】:

在MVVM pattern 上,ViewModel 包含业务逻辑并在需要更新时通知 View。视图也会通知它有关用户事件的信息。

据我了解,每个模型都应该有一个关联的视图模型。所以,如果我们有以下模型:

用户 帐户

我们将拥有以下 ViewModel:

用户视图模型 AccountViewModel

但是,我发现的所有关于使用 MVVM 进行数据绑定的示例都使用单个 ViewModel 进行布局。最近,Google 在Architecture Components 中引入了ViewModel 类。这让我相信一个 Activity 会有一个 ViewModel 可以连接到所有相关的模型:

用户/帐户 --> ActivityViewModel

如果我们想到RecyclerView,情况就会变得更加复杂。每个适配器项本身都可以是一个 ViewModel,因此具有 RecyclerView 的 Activity 将在列表中具有多个 ViewModel,并为剩余的视图内容加上一个主视图(假设它们需要来自 ViewModel 的信息)。例如:

在此示例中,我们有一个 Account ViewModel 列表和一个 UserViewModel。这将如何嵌入到单个 ActivityViewModel 中?

【问题讨论】:

【参考方案1】:

用户和帐户之间是什么关系?如果这是两个独立的、不相关的模型,那么它们应该都有自己的视图和视图模型。记住单一职责原则:每个模块应该只负责你的逻辑的一个部分。这样,对您的域逻辑或模型的任何更改都只会影响该部分,并且仅影响该部分。

【讨论】:

经过更多考虑,我同意每个模型都应该有自己的 ViewModel。但是,我仍然发现每个模型都有一个视图很难。 Activity 可能想要展示多个模型的一部分,我们可以有一个与多个 ViewModel 连接的视图。【参考方案2】:

Google 建议您在每个 View(即 ActivityFragment)中使用 1 个 ViewModel(请参阅 https://youtu.be/Ts-uxYiBEQ8?t=8m40s),然后在每个 ViewModel 中您可以拥有超过 1 种类型的模型.然而,MVVM 的原则是每个ViewModel 只能有 1 个模型类型,因此 Google 的介绍与 :/ 相矛盾。我想您必须决定哪种方法更适合您的应用。

关于您提到的列表示例,对于您将使用paging library 的列表,您不会这样做。您可以在我上面链接的视频末尾看到有关如何使用它的详细信息。

【讨论】:

【参考方案3】:

一个视图模型是标准的。但是,即使是 google 也建议您可能拥有多个视图模型。当您将手机应用程序迁移到平板电脑应用程序时,这非常方便。当您在单个选项卡视图上组合多个电话视图时,可以方便地重新使用模型。

如果您在编码时牢记 SOLID 原则,那么 S(类的单一职责)可能会导致您使用多个视图模型。

但是,每个视图一个非常标准,如上所述,您应该有理由使用多个视图。

【讨论】:

我有一个活动显示多个片段。这个活动有一个视图模型,它的唯一目的是向服务器请求数据,这些请求由片段调用。我应该按片段拆分我的视图模型吗?我还在 viewModel 中创建了我的 LiveData 对象,并且片段会观察这个 LiveData 来绘制视图。它是正确的?我正在使用 viewmodel 来尝试学习架构组件,但我不知道我是否以一种好的方式使用它。谢谢。 @PabloR。这很难从远处说,你是否以聪明的方式应用模式。我认为在简单的情况下,使用一个视图模型并在所有片段之间共享它是公平的。当它有点复杂时,特别是当为不同的片段调用几个不同的服务时,我会为每个片段提供一个 VM。然后,VM 可以共享一个用例类(或远程源类),该类将为视图模型提供服务调用。 谢谢@Herman。就我而言,服务调用不同于任何片段。我有 2 个活动(登录和主) LoginViewModel 调用 2 个不同的 API,而 MainViewModel 只有一个。所以如果它变得更复杂,我会保持这样。 好吧,当调用不同时,共享一个公共 VM 没有任何优势。如果您真的想在一个后端外观中将所有调用放在一起,那么单独进行,让每个视图模型调用该后端外观的方法。使外观可注入到视图模型中,并且您拥有完全可测试的视图模型。【参考方案4】:

每个视图(活动、片段或自定义视图)应该有一个 ViewModel,有多个 LiveData,每个逻辑单元一个。在图像中,一个逻辑单元是用户数据,另一个逻辑单元是设置数据,因此您将在 ViewModel 中公开两个 LiveData。

这些概念也可以在上一届 Google I/O 中推荐的谷歌应用架构中看到,其中一个 Activity/Fragment 有 1 个 ViewModel 和多个 LiveData:

【讨论】:

如果您想知道如果我们有多个视图(可能是活动或片段),我应该为每个活动或片段使用 1 个 ViewModel 吗?答案是肯定的,仅仅是因为它可以帮助您的代码进行维护,而代码重复的可能性很小。 IMO 代码复制总是比维护一个巨大的单个文件代码库更好。您可以在这些小视图模型之上拥有一个父 ViewModel,以便在访问存储库时更好地控制。如需更多帮助,请参阅这个解释得很漂亮的答案。 softwareengineering.stackexchange.com/a/308592/386895

以上是关于在 Android 上使用 MVVM 时,每个 Activity 是不是应该有一个(且只有一个)ViewModel?的主要内容,如果未能解决你的问题,请参考以下文章

在 android 中使用 MVVM 设计模式时出错找不到符号类 ViewModel

Android MVVM UI 控件

Android 用MVVM框架模式+DataBinding+JSON来查询杭州天气信息(更新中)

Xamarin.Android和UWP之MVVM的简单使用

使用 Android MVVM 查看数据绑定集合

Android RecyclerView MVVM 在哪里使用 notifyDataSetChanged 更新 Adapter