本篇文章不关注ViewModel是如何使用的,主要讨论的是ViewModel的原理以及它是如何创建的
我们在创建ViewModel
的时候,有多种方式,比如,我们可以直接调用它的构造方法
val model = MyViewModel()
我们也可以使用下面的方法来创建
val model = ViewModelProviders.of(this).get(MyViewModel::class.java)
注意,使用ViewModelProviders
需要导入下面的依赖
implementation "android.arch.lifecycle:extensions:1.1.1"
说明:不推荐直接调用ViewModel
的构造参数去创建一个ViewModel
,因为ViewModel
的主要目的是去保存数据,比如在我们的Activity
翻转的时候,Activity
的onDestroy
方法会被调用,然后Activity
的构造方法会被调用,然后Activity
的onCreate
方法会被调用,ViewModel
的目的就是确保,销毁前的Activity
与重新创建的Activity
可以获取到同一个ViewModel
,从而达到数据保存的目的,我们通过ViewModelProviders.of(..).get(..)
的方式,就可以确保获取到的ViewModel
是同一个ViewModel
,但是,如果我们是直接调用ViewModel
的构造参数去获取ViewModel
,那么销毁前的Activity
与重新创建的Activity
获取到的不是同一个ViewModel
。
最近,发现了有一种新的创建ViewModel
的方式,就是调用ViewModelProvider
的构造方法,创建一个ViewModelProvider
,注意不是**ViewModelProviders
**
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
然后调用ViewModelProvider
的get
方法,传入要构造的ViewModel
的Class
对象,去获取一个ViewModel
@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);
}
那么,我们的疑问是,ViewModelProvider
构造参数中的ViewModelStore
、Factory
是什么呢?它与我们使用ViewModelProviders.of(..).get(..)
的方式创建ViewModel
有什么联系吗?我们后面会一一进行分析。
我们先分析ViewModelProviders.of(..).get(..)
是如何创建ViewModel
的,这有利于我们后面的理解。
我们使用下面的语句来创建ViewModel
val model = ViewModelProviders.of(this).get(MyViewModel::class.java)
首先查看ViewModelProviders.of
方法,这个方法用于创建ViewModelProvider
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
调用了自己的重载方法,第二个参数传入了null
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
// 检查是否能获取到Application,如果可以则获取,不可以则抛出异常
Application application = checkApplication(activity);
// 若factory为null,那么就获取AndroidViewModelFactory,并赋值为factory
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
// 获取activity的ViewModelStore,然后创建ViewModelProvider,构造参数为
// ViewModelStore和factory
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
AndroidViewModelFactory
是创建ViewModel
的一个工厂,ViewModelStore
用于存储ViewModel
,getViewModelStore
方法定义在ViewModelStoreOwner
接口中,ComponentActivity
实现了该接口
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
另外,Fragment
类也实现了ViewModelStoreOwner
接口,在Fragment
中也可以调用getViewModelStore
方法获取对应的ViewModelStore
。
上面提到AndroidViewModelFactory
,它的定义如下
public static class AndroidViewModelFactory extends
ViewModelProvider.NewInstanceFactory {
// AndroidViewModelFactory采取单例的方式进行定义
private static AndroidViewModelFactory sInstance;
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application
application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
// AndroidViewModelFactory持有Application的引用
private Application mApplication;
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
// 创建ViewModel的方法
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
try {
return modelClass.getConstructor(Application.class)
.newInstance(mApplication);
} catch ...
}
return super.create(modelClass);
}
}
继承关系:AndroidViewModelFactory -> NewInstanceFactory -> Factory
,它们都定义在ViewModelProvider
里面,Factory
接口定义了create
方法,用于创建ViewModel
。
我们上面调用了ViewModelProvider
的构造方法,它里面其实就是将ViewModelStore
和Factory
保存起来了
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
总结一下,ViewModelStore
就是Activity
中的ViewModelStore
,它用于存储ViewModel
,由于我们没有主动指定Factory
,所以这里Factory
就是系统默认的AndroidViewModelFactory
。
调用ViewModelProvider
的get
方法,就可以创建ViewModel
@NonNull
@MainThread
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);
}
调用了自己的重载方法get
,传入两个参数,第一个参数是key
,用于标识ViewModel
,这里使用ViewModel
的「包名+类名」来标识ViewModel
,第二个参数是ViewModel
对应的Class
对象
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
// 在ViewModelStore中查找是否有key对应的ViewModel
ViewModel viewModel = mViewModelStore.get(key);
// 该方法类似于instanceof的语句,如果viewModel对应的类型,是modelClass对应的类型,或者
// 是modelClass的子类类型,那么该方法返回true,否则返回false。如果viewModel为null,该方
// 法也返回false
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 {
// 利用AndroidViewModelFactory去创建一个ViewModel
viewModel = (mFactory).create(modelClass);
}
// 将ViewModel存入ViewModelStore中
mViewModelStore.put(key, viewModel);
//noinspection unchecked
// 返回ViewModel
return (T) viewModel;
}
这里我们需要关注三点:
AndroidViewModelFactory
如何创建ViewModel
的ViewModelStore
是如何缓存ViewModel
的ViewModelStore
是如何获取的上面调用了AndroidViewModelFactory
的create
方法创建了一个ViewModel
,它的create
方法如下
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
// 如果AndroidViewModel是modelClass对应的类型的父类,该方法返回true,进入If
// 语句,否则该方法返回false
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch ...
}
// 调用父类的create方法
return super.create(modelClass);
}
其实AndroidViewModel
就是内部包含了一个Application
对象的ViewModel
,一般我们使用ViewModel
的时候也没有去继承AndroidViewModel
,所以这里我们就认为它不会进入If
语句,而是直接调用了父类的create
方法。
AndroidViewModelFactory
的父类是NewInstanceFactory
,它的create
方法如下
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
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);
}
}
}
我们可以看到这里调用了modelClass
的newInstance
方法,也就是modeClass
对应的ViewModel
需要有一个空参数的构造方法,否则就会构造失败,抛出异常。
所以,如果我们使用ViewModelProviders.of.get
的方式构造ViewModel
,那么只可以构造出无参数的ViewModel
,如果我们的ViewModel
的构造方法是有参数的,通过ViewModelProviders.of.get
的方式将会构造失败,这时候我们就需要自定义Factory
来构造ViewModel
。
我们直接看下ViewModelStore
的源码
public class ViewModelStore {
// 存储ViewModel的Map
private final HashMap<String, ViewModel> mMap = new HashMap<>();
// 存入ViewModel
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
// 如果有旧的ViewModel与key对应,就调用旧的ViewModel的onCleared方法
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
// 根据key获取ViewModel,若key没有对应的ViewModel,该方法会返回null
final ViewModel get(String key) {
return mMap.get(key);
}
// 返回key的集合
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
// 调用所有ViewModel的clear方法,通知它们可以销毁了,并且清理干净mMap
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
ViewModelStore
是在创建ViewModelProvider
的时候获取的
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
这里调用了activity
的getViewModelStore
方法获取ViewModelStore
,该方法如下
@NonNull
@Override
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是否为null
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
// 查看是否可以从nc恢复ViewModelStore
mViewModelStore = nc.viewModelStore;
}
// 若没有恢复成功,就新创建一个ViewModelStore
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
// 返回ViewModelStore
return mViewModelStore;
}
这里我们分为三种场景分别讨论ViewModelStore
的获取
Activity
是新创建的:这时候mViewModelStore
和nc
均为null
,那么就新构造一个ViewModelStore
并返回。Activity
已经创建了一个mViewModelStore
,但是Activity
由于配置更改,例如横竖屏的切换,导致Activity
销毁又重建:这时候mViewModelStore
为null
,但是nc
不为null
,并且从nc
当中获取到的viewModelStore
也不为null
,这时候重建的Activity
和销毁前的Activity
拿到的ViewModelStore
是同一个,然后就可以通过同一个ViewModelStore
,去获取之前的ViewModel
,以达到配置更改但是数据得到保存的目的。Activity
已经创建了一个mViewModelStore
,这时候我主动去销毁该Activity
,然后又重新启动该Activity
:该场景和场景1一样,即mViewModelStore
和nc
均为null
,那么就新构造一个ViewModelStore
并返回。到了这里,我们就明白了
ViewModelProviders.of.get
的方式是如何做到,在横竖屏切换时,销毁前的Activity
与重新创建的Activity
可以获取到同一个ViewModel
。ViewModelProviders.of.get
默认只可以构造没有参数的ViewModel
,如果ViewModel
带有参数,我们就需要自定义构造ViewModel
的Factory
。例如现在我们的ViewModel
构造方法是带有参数的
class MyViewModel(val arg: Int) : ViewModel() {
...
}
那么我们就需要自定义一个Factory
,在它的create
方法中,调用MyViewModel
的带有参数的构造方法,创建MyViewModel
并返回
class VMFactory(val arg: Int) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MyViewModel(arg) as T
}
}
接着在Activity
当中,我们只需要主动地去构造ViewModelProvider
,传入我们自定义的Factory
,然后调用ViewModelProvider
的get
方法,即可获取MyViewModel
的实例
class ViewModelActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
val model = ViewModelProvider(viewModelStore,
VMFactory(10)).get(MyViewModel::class.java)
...
}
}
这里构造ViewModelProvider
是使用了和ViewModelProviders.of.get
一样的构造方法
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
但是传入一个ViewModelStore
总归是有点别扭,其实ViewModelProvider
还有另外一个构造方法
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
这个方法中,我们只需要传入一个ViewModelStoreOwner
,然后它就会获取owner
的ViewModelStore
,然后自动调用上面的含ViewModelStore,Factory
参数的构造方法。
ComponentActivity
和Fragment
都实现了ViewModelStoreOwner
接口,可以调用getViewModelStore
方法,获取ViewModelStore
。