分配职责
应用架构组件中典型的实体交互
理想情况下,ViewModels 不应该对Android框架类有耦合。这提高了可测试性、泄漏安全性和模块性。一般的经验法则是确保你的ViewModels 中没有导入像 android.( android.arch. 例外)。presenters也一样。
不要让ViewModels(和 Presenters)知道Android框架类的存在
条件语句、循环和业务逻辑应该在ViewModels或应用程序的其他层中进行,而不是在Activity 或 Fragment中进行。View层通常不可单元测试(除非使用Robolectric),所以代码行越少越好。View层只应该知道如何显示数据并将用户事件发送到ViewModel(或Presenter)。这被称为被动视图模式。
把Activity 或 Fragment中的逻辑保持到最少
ViewModel的视图引用
ViewModel具有不同于Activity的作用域。当ViewModel处于活动状态并运行时,Activity可以处于任何生命周期。在ViewModel不知道的情况下,Activity和Fragment可能被销毁和再次创建。
配置更改时ViewModel持续存在
将View(Activity 或 Fragment)的引用传递给ViewModel有严重的风险。假设ViewModel请求来自网络的数据,数据稍后回来。此时,引用的视图可能被销毁,或者可能是不再可见的旧Activity,从而产生内存泄漏,并且可能导致崩溃。
避免在ViewModel中引用View
在ViewModels和Views之间进行通信的推荐方式是观察者模式,使用LiveData或其他库。
观察者模式
在Android中设计Presenter 层的一个非常方便的方法是让View(Activity 或 Fragment)观察(订阅更改)ViewModel。由于ViewModel不需知道Android,它不知道Android是如何频繁地杀掉视图的。这样有一些优点:
- ViewModel在配置更改时保持不变,因此当发生旋转时,不需要重新查询外部数据源(例如数据库或网络)。
- 当长时间运行的操作完成时,ViewModel中的可观测项被更新。不管数据是否被观察,都不重要。当试图更新不存在的视图时,不会出现空指针异常。
- ViewModel不引用视图,因此内存泄漏的风险更小。
private void subscribeToModel() {
// Observe product data
viewModel.getObservableProduct().observe(this, new Observer<Product>() {
@Override
public void onChanged(@Nullable Product product) {
mTitle.setText(product.title);
}
});
}
典型的订阅代码
让UI观察到它的变化,而不是将数据推到UI上
臃肿的ViewModel
如果你的ViewModel持有太多代码或太多的责任,考虑:
- 将一些逻辑移到presenter,与ViewModel具有相同的范围。它将与应用程序的其他部分进行通信,并更新ViewModel中的LiveData。
- 增加一个Domain 层,采用了清晰的体系结构。这构成一个非常可测试和可维护的体系结构。
分配职责,如果需要的话添加domain 层
使用数据仓库
正如在 Guide to App Architecture 中看到的,大多数应用程序都有多个数据源,如:
远程:网络还是云
本地:数据库或文件
内存缓存
如果有多功能的且非常不同的数据模型,考虑添加多功能的存储库。
数据状态处理
考虑这个场景:你正在观察一个LiveData ,它包含一个要显示的列表。视图如何在加载数据、网络错误和空列表之间进行区分?
可以从ViewModel中公开LiveData < MyDataState>。例如,MyDataState可以包含有关数据是否正在加载、已加载成功或失败的信息。
使用包装或另一个LiveData 公开有关数据状态的信息
保存Activity状态
Activity状态是如果一个Activity消失了,即该活动被销毁或进程被终止,则需要重新创建屏幕的信息。旋转是最明显的情况,我们已经看到了ViewModel。状态是安全的,如果它是保存在ViewModel。
然而,在其他ViewModel也消失的场景中,您可能需要恢复状态:例如,当OS资源不足并终止进程时。
为了有效地保存和恢复UI状态,使用持久性、onSaveInstanceState()和ViewModel的组合。请参阅:ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders
ViewModel泄漏
UI层使用观察者模式,数据层使用回调
如果用户退出应用程序,View将消失,因此不再观察ViewModel 。如果数据仓库是单例,或者以其他方式限制应用程序的范围,则数据仓库将不会被销毁,直到进程被终止。只有当系统需要资源或用户手动杀死应用程序时才会终止。如果数据仓库中持有对ViewModel 回调的引用,则ViewModel 将暂时泄漏。
Activity关闭,但ViewModel 仍在
理想情况下,当没有任何观察者,ViewModel都应该自由地销毁:
有很多选择来实现这一点:
- 使用ViewModel.onCleared(),可以告诉数据层删除ViewModel回调。
- 在数据层中,使用弱引用,或者使用事件总线(容易误用,甚至被认为有害)。
- 使用LiveData在数据层和ViewModel之间进行通信,就像在View和ViewModel之间使用LiveData一样。
考虑边缘情况、漏洞以及长时间运行的操作如何影响体系结构中的实例。
不要把逻辑放在ViewModel,这对于保存状态清晰或数据相关至关重要。
数据层中的LiveData
为了避免泄露ViewModel和回调地狱,可以像这样观察数据层:
当ViewModel被清除或View生命周期结束时,订阅被清除:
每当你需要一个ViewModel内的生命周期对象时,Transformation可能是解决方案。
扩展LiveData
LiveData最常见的用例是在ViewModel中使用MutableLiveData ,并将它们暴露为LiveData,以使它们不受观察者的影响。
如果你需要更多的功能,扩展LiveData可知道什么时候有活跃的观察者。例如,当想开始侦听位置或传感器服务时,这是有用的。
public class MyLiveData extends LiveData<MyData> {
public MyLiveData(Context context) {
// Initialize service
}
@Override
protected void onActive() {
// Start listening
}
@Override
protected void onInactive() {
// Stop listening
}
}