本文会基于最新版ViewModel使用方法与源码进行详细分析,从注册到实现ViewModel界面数据如何保存与管理全部涉及。
**
简介:
**ViewModel
是JetPack系列库之一,它用来对组件的界面数据进行管理,且当组件的状态发生改变时数据依然留存。
优点:1.当所依赖组件的状态发生改变时,例如屏幕旋转等,界面数据不会发生改变
2.实现MVVM架构的基础,在日常开发中,ViewModel
就是MVVM架构的VM层,它相当于MVP架构的present,我们可以将业务逻辑以及数据交互的部分放入ViewModel中。
源码分析:
版本:viewmodel:1.1.1
此处使用的示例代码与livedata源码分析的相同,不影响流程分析
首先构造一个继承于ViewModel
的类
接着创建一个factory
实例传入ViewModelProvider(this,factory )
中去并调用get
方法将我们的ViewModel
作为参数传入。
此处需要注意的是在以前的版本中直接通过 ViewModelProviders.of(this).get()
方法来完成这个操作的。在最新的版本中已经取消了of方法,并且构造方法必须要传入2个参数
查看ViewModelProvider
的构造方法可看出构造ViewModelProvider
时必须传入2个参数才行,其中第一个参数为ViewModelStore
,第二个参数为factory,ViewModelStore
用来存储和管理ViewModel
,factory
则用来对ViewModel
进行实例化。此时构建ViewModelProvider
时传入的参数为所依赖的组件的ViewModelStore
,也就是说,我们通过组件的ViewModelStore
来对ViewModel
进行存储和管理。
其中factory
需要我们在构造的时候进行传入,ViewModelProvider
给我们提供了3种实例化的方法,分别是简单工厂模式构造,通过反射构造实例,以及我们自己实现factory构造实例。
当我们使用ViewModelProvider.NewInstanceFactory()
构造实例时,查看源码可看出,它实现了Factory
接口,并且在其内部的create
方法中会调用我们在之前get
方法中传入的viewmodel
的newInstance
方法。这个方法需我们自己在viewmodel
中去实现,用来返回viewmodel
实例,如果实例化出错则会抛出异常
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
//返回modelClass实例对象
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
使用AndroidViewModelFactory
方式反射生成ViewModel
,该Factory
使用单例模式进行构建 ,所以它的生命周期等同于应用程序的生命周期,且只有一个实例,推荐使用此模式来获取factory
,当它的create
方法被调用时,会通过application利用反射创建一个ViewModel
实例并返回。
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
public AndroidViewModelFactory(@NonNull Application application) {
//构造时进行传入
mApplication = application;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
//通过反射创建viewmodel
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
}
第三种方式就是直接实现Factory
接口,并自己实现create
方法,我们需要在该create
方法内,返回viewmodel
的实例
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
当我们在构造ViewModelProvider
时,会将我们传入的这2个参数进行保存,
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
当我们调用get
方法时
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
//获取到ViewModel的类名
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
//再次进行回调
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
在该方法内首先会从ViewModelStore
中通过key去获取ViewModel
,这个key
就是上面的DEFAULT_KEY
+类名。如果找到则直接返回,否则调用factory
的create
方法创建ViewModel
实例放入ViewModelStore
进行管理和保存并返回该实例
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//从ViewModelStore中获取viewModel
ViewModel viewModel = mViewModelStore.get(key);
//如果找到则直接返回
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
ViewModelStore
内部通过hashmap
来对viewmodel
进行管理,
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
到此处为止源码部分分析完毕,如何触发viewmodel
对组件界面的状态保存继续分析
上面说过在构建ViewModelProvider
时传入的ViewModelStore
来自于构建它所传入的this,也就是当前组件,我们知道ViewModelStore
起到了对viewmodel
的管理作用,viewmodel
的获取和存储都是通过它来完成。
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
在这里调用了组件的owner.getViewModelStore()方法
this(owner.getViewModelStore(), factory);
}
追踪getViewModelStore
方法,发现它是一个ViewModelStoreOwner
接口中的一个方法
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
这里发现它的实现类有很多个,我们一般在ViewModelProvider
中传入的是fragment
或者activity
,而fragment其实最终也是调用的activity的getViewModelStore
方法这里查看activity
如何处理。
发现在activity的父类ComponentActivity
中有这个方法的实现,通过getViewModelStore
去获取一个ViewModelStore
,当ViewModelStore
获取为空时,会调用getLastNonConfigurationInstance
方法去恢复状态改变前的NonConfigurationInstances
,和这个方法对应的是onRetainNonConfigurationInstance
,该方法会在组件状态改变时去保存数据信息,由系统调用。
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.");
}
//如果当前组件的mViewModelStore 为空,则从NonConfigurationInstances 中去获取
if (mViewModelStore == null) {
//返回之前
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
//如果在NonConfigurationInstances 中没有找到,则创建一个新的ViewModelStore并保存起来
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
在代码中ViewModelStore
会从NonConfigurationInstances
中去尝试获取,我们查看viewModelStore
的赋值时机即可
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
查看发现它的赋值时机就是在onRetainNonConfigurationInstance
中完成的,也就是说在组件的状态发生改变时,会去尝试获取viewModelStore
,并将viewModelStore
保存在NonConfigurationInstances
中并返回。
也就是在onRetainNonConfigurationInstance
中会对viewModelStore
进行保存,在getLastNonConfigurationInstance
中会对viewModelStore
进行恢复
具体流程:
当我们在activity
中去实例化ViewModelProvider
时,所依赖的activity在其内部会调用getViewModelStore
方法去获取一个ViewModelStore
,如果没有则创建一个,并保存起来,然后调用ViewModelProvider.get
方法将我们的viewmodel
实例通过ViewModelStore
进行保存管理,当我们的activity的状态发生改变,如旋转屏幕,此时系统会调用onRetainNonConfigurationInstance
方法,在这个方法内会将我们的ViewModelStore
进行保存。
我们一般会搭配livedata
来使用viewmodel
,livedata
常用来存储与界面相关的数据元素,当我们的activity状态改变后,界面再次从livedata
中获取界面数据时,由于livedata
实例由viewmodel
保管,而viewmodel
存储在ViewModelStore
中,当前activity去获取ViewModelStore
时会通过getLastNonConfigurationInstance
方法恢复之前的ViewModelStore
,所以状态改变前后的ViewModelStore
是同一个,所以数据没有发生任何改变,从而界面相关数据不会发生丢失。