简介
ViewModel
的作用如它的名字所示,用来存储View
相关的数据,并且他会自动感知当前界面的生命周期,以便在界面重建的时候能够及时恢复数据(如屏幕旋转、语言等Configuration变化的时候)
一分钟上手
我们来简单看下他的使用方法,真的非常简单,首先实现一个自定义的ViewModel
(先忽略LiveData
,可把它理解为一个数据的包装器):
1 | public class CustomViewModel extends ViewModel { |
随后使用ViewModelProvider.get(Class<T>)
方法即可获取到对应的ViewModel
实例,结束,就是这么简单。当界面重建并再次调用此方法获取ViewModel
的时候,将会拿到之前Activity
创建的实例。
1 | CustomViewModel model = ViewModelProviders.of(this).get(CustomViewModel.class); |
RTFSC
是不是觉得很神奇,当界面重建的时候,数据被存到哪了呢?让我们一同翻翻它的源码来找答案~
ViewModel的生命周期
先来简单熟悉下主角ViewModel
的生命周期,可以看到,在界面真正onDestroy()
之前,ViewModel
是会一直存活的。也就是说ViewModel
的生命周期会比Activity
的长,为了避免内存泄漏,我们千万不能随便持有Activity
的引用。
ViewModel vs AndroidViewModel
首先看下ViewModel
与AndroidViewModel
这两个类,实现都非常简单。其中onCleared()
方法会在ViewModel
不再使用并即将销毁的时候调用。AndroidViewModel
继承ViewModel
,比它多了一个Application
成员变量。因此,当我们需要在ViewModel
中使用Context
的时候,就必须选用AndroidViewModel
,否则就很容易出现内存泄漏。
1 | public abstract class ViewModel { |
ViewModelProvider
回到生成ViewModel
实例的那段代码ViewModelProviders.of(this).get(CustomViewModel.class)
,我们是通过ViewModelProviders
的of(FragmentActivity)
方法来获取ViewModelProvider
。
1 | public static ViewModelProvider of(@NonNull FragmentActivity activity) { |
当我们没有提供Factory
的时候,默认会使用AndroidViewModelFactory
来生成ViewModel
对象,其中Factory
是一个工厂方法接口,用于产生对应类型的ViewModel
对象。
1 | public interface Factory { |
可以看到AndroidViewModelFactory
的实现十分简单粗暴,如果class是AndroidViewModel
类型的,就直接调用带Application
的构造器new对象,否则调用默认构造器new对象。若我们的ViewModel
需要更多的参数才能创建,在上一步的of(FragmentActivity)
方法中就必须传入自定义的Factory
类了。
1 | public static class AndroidViewModelFactory extends NewInstanceFactory { |
成功创建了ViewModelProvider
后,我们就可以通过get(Class<T>)
方法来获取一个ViewModel
了。从实现来看也非常简单,通过key从ViewModelStore
中拿出对象,如果不存在则创建一个新的丢进去并返回。
1 | public <T extends ViewModel> T get(@NonNull Class<T> modelClass) { |
ViewModelStore
接下来看ViewModelStore
的实现,从类名来看就是一个存储ViewModel
的地方,实现也还真的非常简单,采用了HashMap
来存储对象。
1 | public class ViewModelStore { |
我们回去看看ViewModelStore
到底是怎么被创建的,是通过ViewModelStores.of(FragmentActivity)
方法创建的。我们来瞧一瞧它的实现代码:
1 | public static ViewModelStore of(@NonNull FragmentActivity activity) { |
这里需要区分下Support Library的版本,如果在v27.1.0版本及以上的,FragmentActivity
已经实现了ViewModelStoreOwner
接口,所以返回的是activity.getViewModelStore()
1 | public ViewModelStore getViewModelStore() { |
那么界面在重建的时候是如何保存这个ViewModelStore
的呢?关键就在onRetainNonConfigurationInstance()
这个方法,系统会自动将其存起来,然后onCreate()
的时候再将其取出。
1 | public final Object onRetainNonConfigurationInstance() { |
如果在v27.1.0以下的版本,则返回的是holderFragmentFor(activity).getViewModelStore()
。首先看下holderFragmentFor()
逻辑,通过FragmentManager
来获取HolderFragment
,如果没有就通过FragmentManager
添加并存储进mNotCommittedActivityHolders
中。为什么叫NotCommitted呢?因为在commit之后不会马上添加完成,这是一个异步的过程,所以先把他存在mNotCommittedActivityHolders
中,以免快速再次调用holderFragmentFor()
方法的时候不能及时拿到commit的HolderFragment
。
1 | HolderFragment holderFragmentFor(FragmentActivity activity) { |
最后一步就是分析HolderFragment
,到底他做了什么可以在重建的时候保存数据呢?关键就在于setRetainInstance(true)
这个方法,它可以在Activity
重建的时候继续存活,以达到保存数据的目的。
1 | public class HolderFragment extends Fragment implements ViewModelStoreOwner { |
总结
至此,ViewModel
如何在Activity
重建的时候成功保存数据就分析完毕了。这里还得注意Support Library的版本问题,如果在v27.1.0及以上版本,是通过FragmentActivity.onRetainNonConfigurationInstance()
方法实现的,而v27.1.0以下的版本则是通过Fragment.setRetainInstance(true)
这个方法实现的,大家需要注意区别。还是觉得onRetainNonConfigurationInstance()
的实现会优雅一点~