

ViewModels and LiveData- Patterns + AntiPatterns
source link: https://blog.csdn.net/eclipsexys/article/details/121867719
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

点击上方蓝字关注我,知识会给你力量
这个系列我做了协程和Flow开发者的一系列文章的翻译,旨在了解当前协程、Flow、LiveData这样设计的原因,从设计者的角度,发现他们的问题,以及如何解决这些问题,pls enjoy it。
Views and ViewModels
Distributing responsibilities

理想情况下,ViewModels不应该知道关于Android的任何事情。这可以提高可测试性、泄漏安全性和模块化。一般的经验法则是,确保在你的ViewModels中没有android.*的导入(android.arch.*等例外)。这同样适用于presenters。
❌ 不要让ViewModels(和Presenters)知道Android框架类的情况
条件语句、循环和一般决策应该在ViewModels或应用程序的其他层中完成,而不是在Activities或Fragments中。视图通常没有单元测试(除非你使用Robolectric),所以代码行数越少越好。视图应该只知道如何显示数据并将用户事件发送到ViewModel(或Presenter)。这就是所谓的被动视图模式。
✅将Activity和Fragment中的逻辑保持在最低限度
View references in ViewModels
视图模型与Activity或Fragment有不同的作用域。当一个ViewModel活着并运行时,一个Activity可以处于其生命周期的任何状态。在ViewModel不知道的情况下,Activity和Fragment可以被销毁并再次创建。

将视图(Activity或Fragment)的引用传递给ViewModel是一个严重的风险。让我们假设ViewModel从网络上请求数据,并且数据在一段时间后回来。这时,View的引用可能会被破坏,也可能是一个不再可见的旧Activity,产生内存泄漏,并可能导致崩溃。
❌ 避免在ViewModels中对View进行引用。
在ViewModels和View之间进行通信的推荐方式是观察者模式,使用LiveData或来自其他库的观察变量方式。
Observer Pattern

在Android中设计表现层的一个非常方便的方法是让View(Activity或Fragment)观察(订阅)ViewModel的变化。由于ViewModel并不了解Android,所以它不知道Android是如何喜欢频繁地杀死View的。这有一些好处。
ViewModel在配置变化时被持久化,所以当重新请求发生时,不需要重新查询外部数据源(如数据库或网络)。
当长期运行的操作结束时,ViewModel中的观察变量会被更新。数据是否被观察并不重要。当试图更新不存在的视图时,不会发生空指针异常。
ViewModels不引用视图,所以内存泄漏的风险较小。
✅不要把数据推送给UI,而是让UI观察到它的变化。
Fat ViewModels
只要能让你分离关注点,就是一个好主意。如果你的ViewModel容纳了太多的代码或者有太多的责任,可以考虑。
将一些逻辑转移到与ViewModel相同范围的presenter中。它将与你的应用程序的其他部分通信,并更新ViewModel中的LiveData持有者。
添加一个Domain layer并采用Clean Architecture。这将导致一个非常可测试和可维护的架构。它也有利于快速离开主线程。在Architecture Blueprints中有一个Clean Architecture的例子。
例子在这里:https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
✅ 分散责任,如果需要的话,添加领域层。
Using a data repository
正如在《应用程序架构指南》中看到的那样,大多数应用程序都有多个数据源,例如。
远程:网络或云
本地:数据库或文件
内存中的缓存
在你的应用程序中设置一个数据层是个好主意,完全不知道你的表现层。让缓存和数据库与网络保持同步的算法并非易事。建议有一个单独的存储库类作为处理这种复杂性的单一入口。
如果你有多个非常不同的数据模型,可以考虑添加多个存储库。
✅ 添加一个数据存储库作为你的数据的单点入口
Dealing with data state
考虑这个场景:你正在观察一个由ViewModel暴露的LiveData,它包含一个要显示的项目列表。视图如何区分正在加载的数据、网络错误和一个空列表?
你可以从ViewModel中暴露出一个LiveData。例如,MyDataState可以包含关于数据是否正在加载、是否已经成功加载或失败的信息。

你可以把数据包装在一个有状态和其他元数据(如错误信息)的类中。参见我们样本中的资源类:https://developer.android.com/jetpack/guide#addendum。
✅使用包装器或另一个LiveData暴露你的数据的状态信息。
Saving activity state
Activity状态是你在一个Activity消失时重新创建屏幕所需要的信息,这意味着该Activity被破坏或进程被杀死。旋转是最常见的情况,我们已经用ViewModels覆盖了这种情况。所以,状态被保存在ViewModel中是安全的。
然而,你可能需要在ViewModels也消失的其他情况下恢复状态:例如,当操作系统资源不足并杀死了你的进程时。
为了有效地保存和恢复UI状态,可以使用持久性、onSaveInstanceState()和ViewModels的组合。
对于一个例子,请看。ViewModels: 持久性、onSaveInstanceState()、恢复UI状态和加载器
https://medium.com/androiddevelopers/viewmodels-persistence-onsaveinstancestate-restoring-ui-state-and-loaders-fc7cc4a6c090
Events
事件是发生一次的事情。ViewModels暴露了数据,但事件呢?例如,导航事件或显示Snackbar信息是只应执行一次的动作。
事件的概念与LiveData存储和恢复数据的方式并不完全相符。考虑一个有以下字段的ViewModel。
LiveData<String> snackbarMessage = new MutableLiveData<>();
一个Activity开始观察这个,ViewModel完成了一个操作,所以它需要更新消息。
snackbarMessage.setValue("Item saved!");
该Activity接收该值并显示Snackbar。这显然是有效的。
然而,如果用户旋转手机,新的Activity被创建并开始观察。当LiveData观察开始时,该Activity立即收到旧的值,这导致消息再次显示出来。
与其试图用库或架构组件的扩展来解决这个问题,不如将其作为一个设计问题来面对。我们建议你把你的事件作为你的状态的一部分。
✅将事件设计成你的状态的一部分。更多细节请阅读LiveData与SnackBar、Navigation和其他事件(SingleLiveEvent案例)。
Leaking ViewModels
反应式范式在Android中运行良好,因为它允许在UI和你的应用程序的其他层之间建立一个方便的连接。LiveData是这个结构的关键组件,所以通常你的Activity和Fragment会观察LiveData实例。
ViewModels如何与其他组件通信由你决定,但要注意泄漏和边缘情况。考虑一下这个图,视图层使用观察者模式,数据层使用回调。

如果用户退出了应用程序,视图就会消失,所以ViewModel就不会再被观察。如果repository是一个单例或其他范围的应用程序,repository将不会被销毁,直到进程被杀死。这只会在系统需要资源或用户手动杀死应用程序时发生。如果repository持有对ViewModel中回调的引用,ViewModel将被暂时泄露。

如果ViewModel是轻量级的,或者操作被保证快速完成,这种泄漏就不是什么大问题。然而,情况并不总是这样的。理想情况下,只要没有任何视图在观察它们,ViewModel就应该是自由的。

你有很多选择来实现这一点。
通过ViewModel.onCleared()你可以告诉repository放弃对ViewModel的回调。
在repository中,你可以使用WeakReference,也可以使用事件总线(两者都容易被滥用,甚至被认为是有害的)。
使用LiveData在存储库和ViewModel之间进行通信,其方式类似于在View和ViewModel之间使用LiveData。
这点用Flow也可以解决。
✅考虑边缘情况、泄漏以及长期运行的操作会如何影响你架构中的实例。
❌ 不要在ViewModel中放置对保存清洁状态或与数据有关的逻辑。你从ViewModel进行的任何调用都可能是最后一次。
LiveData in repositories
为了避免泄露ViewModels和回调地狱,可以像这样观察存储库。

当ViewModel被清除或视图的生命周期结束时,订阅被清除。

如果你尝试这种方法,会有一个问题:如果你不能访问LifecycleOwner,你如何从ViewModel订阅Repository?使用Transformations是解决这个问题的一个非常方便的方法。Transformations.switchMap让你创建一个新的LiveData,对其他LiveData实例的变化做出反应。它还允许在整个链条上携带观察者的生命周期信息。
在这个例子中,当触发器得到更新时,该函数被应用,结果被派发到下游。一个Activity将观察repo,同样的LifecycleOwner将被用于repository.loadRepo(id)调用。
只要你认为你在ViewModel中需要一个Lifecycle对象,一个Transformation可能就是解决方案。
Extending LiveData
LiveData最常见的用例是在ViewModels中使用MutableLiveData,并将它们作为LiveData公开,使它们从观察者那里不可改变。
如果你需要更多的功能,扩展LiveData会让你知道什么时候有活跃的观察者。例如,当你想开始监听一个位置或传感器服务时,这很有用。
When not to extend LiveData
你也可以使用onActive()来启动一些加载数据的服务,但除非你有很好的理由,否则你不需要等待LiveData的观察。一些常见的模式。给ViewModel添加一个start()方法,并尽快调用它:https://github.com/android/architecture-samples/blob/dev-todo-mvvm-live/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskFragment.java#L64
设置一个启动加载的属性:https://github.com/android/architecture-components-samples/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/repo/RepoFragment.kt
❌ 你通常不会扩展LiveData。让你的Activity或Fragment告诉ViewModel何时开始加载数据。
原文链接:https://medium.com/androiddevelopers/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54
向大家推荐下我的网站 https://xuyisheng.top/ 点击原文一键直达
专注 Android-Kotlin-Flutter 欢迎大家访问
本文原创公众号:群英传,授权转载请联系微信(Tomcat_xu),授权后,请在原创发表24小时后转载。
< END >
作者:徐宜生
更文不易,点个“三连”支持一下👇
Recommend
-
161
How to find antipatterns in your vim workflow? Closed. This question...
-
6
Software Development AntiPatterns Good software structure is essential for system extension and ma...
-
7
Antipatterns of Software engineering courses I am very honored to be in charge of the Sofware engineering and distributed applications course at the Ecole normale superieure...
-
14
Q&A on the Book Retrospectives Antipatterns ...
-
4
Sponsored by Cockroach Labs What you build and where it takes you shouldn't be limited by your database. CockroachDB helps developers build and scale apps with fewer obstacles, more freedom, and greater efficiency. So you can...
-
9
antipatterns.ko: the world's worst kernel module caution:DO NOT LOAD THIS MODULE This kernel module contains numerous security vulnerabilities. It is intended purely as a testbed for vulnerability detection tools. The i...
-
8
Product Design Antipatterns: Memes That Made Me LaughStart or end your work day with joy!"Don't worry; I know how to do your job."
-
9
StateFlow and SharedFlow in ViewModels December 28, 2022
-
3
January 31, 2023 Register MAUI Views and ViewModels with Scrutor ...
-
6
🚀 Why you need ViewModels and why you don't
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK