ViewModel相关详解
本文已参与「新人创作礼」活动,一起开启创作之路。
一、ViewModel的基础概念
ViewModel主要用于存放页面相关数据,其生命周期与Activity不同,只有当Activity退出时,才会一起被销毁。
当Activity受配置更改的影响而重建时,ViewModel中的数据并不会丢失,使得页面的重建更为简单快速。
同一Activity中的Fragment可以共享ViewModel中的数据,通信方便。
viewmodel生命周期:
二、ViewModel实际运用
1、创建自己的ViewModel,继承ViewModel类即可,在内部丰富属性和方法,用于存储和操作数据
2、简单实现ViewModel,因为需要和Activity绑定,所以不可直接通过构造函数实现,需要通过ViewModelProvider来实现。
3、由于不能直接通过构造函数实例化ViewModel,故不能直接通过带参构造函数对ViewModel中数据进行初始化,此功能可通过自定义factory实现
三、详解ViewModel与Factory
查看ViewModelProvider的源码可以知道,其内部重载有三个构造函数,前者为ViewModel的存储器,后者为Factory。其中,前者我们传入的是Activity,所以这里的owner其实是Activity;另外,如果没有指定Factory,则会在owner中查找是否含有Factory,若没有,则指定一个NewInstanceFactory,也就是说,如果我们只是简单是传入Activity或者Fragment,则会指定为NewInstanceFactory
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);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
看一下NewInstanceFactory里面是什么东西。其内部真正有用的是在create方法中,通过反射实例化一个class的对象
public static class NewInstanceFactory implements Factory {
private static NewInstanceFactory sInstance;
@NonNull
static NewInstanceFactory getInstance() {
if (sInstance == null) {
sInstance = new NewInstanceFactory();
}
return sInstance;
}
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
try {
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);
}
}
}
到此,能获得的信息有两个:
其一是将传入的Activity作为ViewModel的存储器,其二是指定了一个Factory,用于初始化。那这两个功能在哪实现的呢,那就要看get里面的代码了,ViewModel就是通过,将自身的class,传入ViewModelProVider的get方法获得的嘛
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
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);
return (T) viewModel;
}
可以看到,我们传入Class的那个get方法,获取到Class的名字之后,通过与DEFAULT_KEY拼成了一个key,和Class一起传入了自己的重载。重载后的get方法,逻辑如下:
在mViewModelStore中通过key获取VIewModel,获取到的这个ViewModel如果与传入get中的ViewModel类型相同,则return此ViewModel,否则,则判断Factory的类型是否为KeyedFactory(这个类也是ViewModelProVider中的一个内部类,其中有一个带有key和class两个参数的抽象方法create),因为前面过来的是NewInstanceFactory是继承自Factory类,所以直接执行create方法,即执行了上文中提到的反射获得对象,然后将这个key和ViewModel存入ViewModelStore中(这个ViewModelStore是在Activity中获取到的,也就是说,将这个ViewModel存入了Activity)。
由此我们可以知道:
1、Activity中有一个ViewModelStore(在祖宗类ComponentActivity中声明的属性),用于存储此Activity中所有的ViewModel,并且以他们的名字为Key存储,同类型的ViewModel只能储存一个,以此保证了ViewModel的一致行,数据安全性。
2、如果不指定Factory,则会默认执行最简单的Factory进行ViewModel的实例化,无法对ViewModel实例化的时候进行数据的初始化设置,想要实现此功能,可以自己声明指定一个Factory,重写内部的create方法则可。自定义的Factory可以继承ViewModelProvider.Factory类或者ViewModelProvider.KeyedFactory类都可,两者内部方法有细微差别,此处不做深入讨论。