前言
最近几天把Jetpack中的3剑客Lifecycle+ViewModel+LiveData的使用和原理学习了一遍,这3者也是构建MVVM模式的核心。这个系列的工具,建议大家在官网学习:https://developer.android.google.cn/jetpack。
目前网上对这3者使用和源码解析的文章也沉淀了不少,大家可以自行查阅。本文换个方向来跟大家聊聊我所理解的关于ViewModel的一些内容。
系列文章
Android Jetpack ViewModel解析
Android Jetpack LiveData解析
Android Jetpack DataBinding原理浅析(简版)
看看官方对Viewmodel作用的解释
官方的Viewmodel用法例子
官方的解释说明与案例还是很清晰的,所以就不做过多解释了。说一下我在学习过程中的一些疑问吧。
1、在官网和Google的github demo中都建议在mvvm模式中的vm层去继承ViewModel,那为什么一定要继承ViewModel呢?我直接在VM中定义LiveData数据不行吗?是否继承ViewModel的区别在哪?
下面通过一个例子来讲解,这里为了突出核心要点就用近乎伪代码的形式来表述。首先在mvvm的VM
层去获取网络数据,实际上是model
层执行的,然后当获取数据成功后调用LiveData
对象的setValue()
方法通知Activity的观察者去刷新UI。
public class NewsViewModel extends ViewModel {
private NewsModel newsModel;
//adapter数据源
private List<ItemNews> mList = new ArrayList<>();
public MutableLiveData<List<ItemNews>> itemNewsLiveData = new MutableLiveData<>();
public NewsViewModel() {
newsModel = new NewsModel();
}
public void getNews() {
mList = newsModel.getNews();
//通知活跃的观察者更新数据
itemNewsLiveData.setValue(mList);
}
}
Activity里面的观察者
newsViewModel.itemNewsLiveData.observe(this, new Observer<List<ItemNews>>() {
@Override
public void onChanged(@Nullable List<ItemNews> itemNews) {
newsAdapter.updateAll(itemNews);
}
});
这里按照官方的规范,只是在View层持有VM的引用,而VM不持有View的引用。到这一步我就有一个疑问,貌似不继承ViewModel也可以完成VM与View的交互,那引入它到底有什么好处呢?
再回顾下官方的说法:
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。
ViewModel
类让数据可在发生屏幕旋转等配置更改后继续存在。
上面说到ViewModel
管理数据是和生命周期相关的,且能持久存储数据。那下面就简单解析一下是如何与生命周期相关的:
这里先说结论,源码验证待会儿再说。
现在可以看出,好处1:
因为ViewModel
的生命周期是比Activity还要长,所以ViewModel可以持久保存UI数据,具体来说是Activity因为配置更改或者被系统意外回收的时候,会自动保存数据,然后在Activity重建的时候就可以继续使用销毁之前保存的数据。
好处2:
当使用ViewModel
的 Activity 正常退出时,内部会调用ViewModel
对象的onCleared()
方法,以便它可以清理资源。首先内部会释放ViewModel
数据,同时你也可以在vm层重写这个方法去释放资源,取消网络等。
好处3:
在 Fragment 之间共享数据。这个比较简单,只要2个Fragment获取
ViewModel
对象时传入的LifecycleOwner一致就可以共享ViewModel
中定义的数据。
现在回头看刚才的例子,当配置改变导致Activity重建的时候,如果没有使用ViewModel存储数据,那么就会重新请求数据重绘页面;如果用ViewModel的话,就能用页面销毁之前保存的数据去直接显示视图。
2、在MVVM中用了ViewModel+LiveData之后还有必要在Activity/Fragment的onDestory()方法中手动取消耗时任务(网络请求)吗?
引入了ViewModel
和LiveData
之后,可以实现vm
和view
的解耦,只是view引用vm,而vm是不持有view的引用的。在activity退出之后即是还有网络在继续也不会引发内存泄漏和空指针异常,所以不在ondestory
取消网络也是可以的。如果你还是想取消,可以重写onCleared()
方法去做。
原理解析
ViewModel
源码真的很简单,就几个类,实际上实际上真正的业务处理核心就只有ViewModelProvider
,其他都是辅助。
-
ViewModel
类是一个抽象方法,只有一个空方法onCleared()
,它会在onDestory的时候被调用,所以你可以重写这个方法,在里面做些释放资源、取消网络的操作。 -
AndroidViewModel
是继承ViewModel
的,就多了一步,提供Application
参数 -
ViewModelStore
:和名字一样,就是存储ViewModel的,它里面定义了一个HashMap来存储ViewModel,key值是ViewModel全路径+一个默认的前缀。大概这样:key=android.arch.lifecycle.ViewModelProvider.DefaultKey:com.zx.mvvmdemo.vm.NewsViewModel
-
ViewModelStoreOwner
:是一个接口,只有一个方法,是获取ViewModelStore
对象的
ViewModel核心类
通常定义ViewModel之后,通过如下方式获取ViewModel对象
newsViewModel = ViewModelProviders.of(this).get(NewsViewModel.class);
of
传FragmentActivity或者Fragment对象。
这里主要分析2个问题:
- 1、ViewModel是如何创建的?
- 2、ViewModel是如何保存数据的?
这里不按照执行流程去阅读源码那样分析,因为这么详细的内容已经有人写了,我就讲一下关键的思路,细节的话,推荐一遍不错的:
Android ViewModel,再学不会你砍我
从入口来看,是外观模式,ViewModelProviders
就是外观类,真正的业务处理是在ViewModelProvider
注意前者多了一个s
,核心就在ViewModelProvider
的get()
方法
逻辑大致如下:
首先从ViewModelStore的HashMap这个缓存中取取数据,如果有直接返回ViewModel,如果没有通过ViewModelProvider内部的工厂类去创建,ViewModel的创建具体来说是通过反射区做的,并且保存在缓存中,这样就获取到了ViewModel对象。
到这里,问题1就解决了。下面看看问题2,数据是如何保存的。
数据保存是在FragmentActivity中实现的。
因为配置变化导致Activity销毁重建以前的方案是保存数据在onSaveInstanceState
,而还原在onRestoreInstanceState
,但是这2个方法是有很大瑕疵的,于是在新的SDK中FragmentActivity内置了2个新的方法onRetainNonConfigurationInstance
和getLastNonConfigurationInstance
,更多细节可以参考:
https://www.cnblogs.com/dengxianzhi/articles/2248655.html
其中onRetainNonConfigurationInstance
是在onStop() 和 onDestroy()之间被调用,它内部会保存ViewModel数据;而在onCreate的时候会调用getLastNonConfigurationInstance
来恢复数据。
总结
1、让mvvm的vm去继承ViewModel
,并在其内部定义、管理UI所需的数据,不仅可以让View和ViewModel
解耦,同时其内部自动关联生命周期,可以减少在Activity的生命周期方法中写大量的样板代码。
2、ViewModel保存数据这个功能还是有点用的。一开始我觉得当屏幕旋转的时候你可以通过configChanges
的设置来阻止它的重建,这样就不需要viewmodel保存数据了。但是其它的一些意外情况也是有可能导致Activity重建的,比如当前activity在填写一堆表单数据,中间打开了其他app,在后台被回收了,后面再进入的时候如果没有保存就得重填,如果你的UI数据放在ViewModel的话就会自动回复,这样看来,ViewModel的保存数据功能是不是还挺有用呢
3、利用ViewModel共享数据可以更简洁,减少很多不必要的接口。
4、友情提示:在创建ViewModel对象的时候不能手动去new一个,而是通过Provider的方式去获取,这样的话才能利用ViewModel的那些优势。
网友评论