ViewModel
概述
官方对ViewModel的解释:
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。
作用
它在Android中事实上是为了解决一下两个问题:
- UI组件间实现数据共享
- Activity因配置更改重建时保留数据
对于第一条,如果不使用VM,那么各个UI组件都需要持有共享数据的引用,这样会带来两个麻烦:
- 如果新增共享数据,则各个UI组件需要再次声明并初始化新增的共享数据
- 某个组件对于数据的修改,没办法直接通知其他UI组件,需手动实现观察者模式
对于第二条,如果不使用VM,那么还是可以通过onSaveInstanceState保存的,但是如果数据量比较大,数据的序列化和反序列化都会产生一定的性能开销。
ViewModel的特点
和上面作用说的类似。
- ViewModel支持恢复大量数据,还不需要序列化和反序列化。而onSaveInstanceState只适用于少量数据,而且它还需要对数据进行序列化和反序列化。
- 更容易、更有效地将视图数据相关逻辑从视图控制器(Activity和Fragment)中分离。
- 可以避免内存泄漏。它专门用来管理网络请求等异步调用
ViewModel的创建
一般我们会通过下面这行代码创建或获取ViewModel
ViewModelProvider(this).get(MvvmViewModel::class.java)
ViewModelProvider的参数是一个ViewModelStoreOwner对象,ComponentActivity实现了这个接口,它表明实现类会持有一个ViewModelStore对象。
这行代码的作用主要就是从传入的ViewModelStoreOwner对象中取出想要获取的ViewModel类,或通过反射创建ViewModel对象并将其存储在ViewModelStore的Map中。
ViewModelProvider的构造方法中,都会通过ViewModelStoreOwner的getViewModelStore方法来获取ViewModelStore。
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
Activity重建时如何保证ViewModel不重建
主要是通过**onRetainNonConfigurationInstance
**这个方法实现的,通过这个方法可以像onSaveInstanceState()的方法一样保留变化前的Activity State,最大的不同在于这个方法可以返回一个包含有状态信息的Object。
注意的点:
1、onRetainNonConfigurationInstance()在onSaveInstanceState()之后被调用。
2、调用顺序同样介于onStop()和onDestroy()之间(onSaveInstanceState在API28以后在onStop之后被调用)。
在ComponentActivity中,这个方法的声明如下:
//注释说:这个方法用来保存所有合适的非配置状态
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
当系统配置发生改变时,Activity会被销毁并进行重建。
当ActivityThread执行到performDestroyActivity()这个方法时,就会调用Activity的retainNonConfigurationInstances()
将保存的数据保存到ActivityClientRecord中;
当页面重构完成,就会调用ActivityThread的performLaunchActivity()将这个数据重新赋给Activity的mLastNonConfigurationInstances属性。
在Activity重建时,会再次调用ViewModelProvider(this).get(MvvmViewModel::class.java)
这行代码,通过ViewModelStoreOwner的getViewModelStore方法来获取ViewModelStore:
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
//获取Activity销毁前ActivityClientRecord中保存的这个对象
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
//直接从NonConfigurationInstances中获取之前的viewModelStore对象,其中的Map存储了ViewModel的引用
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
因此重建后的新Activity持有的仍是原先的ViewModel对象。
ViewModel的销毁
在ComponentActivity的无参构造方法中有下面这些代码:
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
//如果因为系统配置更改(旋转)导致销毁Activity,则不会清除ViewModelStore中的ViewModel
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
这里向ComponentActivity中的mLifecycleRegistry注册了一个LifecycleEventObserver,在收到Activity销毁的事件时会调用getViewModelStore().clear()方法将ViewModelStore中的ViewModel全部销毁。
ViewModel如何在Activity和各个Fragment内部共享
如果Activity和其中的几个Fragment需要共享一个ViewModel,我们一般会这样来获取ViewModel:
在Activity中:
ViewModelProvider(this).get(MyViewModel::class.java)
//下面通过getViewModelStore方法获取了ViewModelStoreOwner持有的ViewModelStore对象
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
因为ViewModel的实例是存储在Activity(ViewModelStoreOwner)持有的ViewModelStore中的一个HashMap中的,因此这里是获取HashMap中的ViewModel或反射创建ViewModel并放入HashMap中。
在Fragment中获取:
ViewModelProvider(getActivity()).get(MyViewModel.class);
ViewModelProvider中传入的是Fragment对应的Activity,本质上还是从Activity中持有的ViewModelStore中的一个HashMap中来获取的,因此它们获取的是同一个ViewModel对象,可以进行共享。
总结
ViewModel如何保证在Actvity旋转前后获取到的是同一个?
因为在执行onDestroy之前,从ActivityClientRecord持有一条到ViewModelStore引用链,所以当Activity被销毁时,ViewModelStore不会被垃圾回收,也就不会被销毁,而Activity的reLaunch并不会销毁对应的ActivityClientRecord,下次仍然会复用ActivityClientRecord,进而复用保存的ViewModelStore,这样就解释通了;