ViewModel 获取通过
ViewModelProvider(activity)[VM::class.java]
今天来探究下 ViewModel 具体是如何创建出来的。
首先会创建一个 ViewModelProvider ,ViewModelProvider 的构造函数如下:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
ViewModelProvider 里面包括两部分:
ViewModelStore 从 ViewModelStoreOwner 中获取。
ViewModelStoreOwner
是一个接口:
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
以 FragmentActivity
为例,
@Override
public ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
如果 getLastNonConfigurationInstance 不为 null, 则取 NonConfigurationInstances 的 ViewModelStore。创建一个新的ViewModelStore。
获取 ViewModel 示例也是通过 ViewModelProvider
的get 方法:
@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;
}
这时候通过 ViewModelStore 获取 ViewModel 的逻辑:
final ViewModel get(String key) {
return mMap.get(key);
}
ViewModelStore 内部其实是一个 Map 结构:
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);
}
}
回到 ViewModelProvider#get:
当 ViewModelStore 里面存在 ViewModel 的时候,会直接复用这个 ViewModel 实例。
如果不存在,则会使用 factory 去创建我们的 ViewModel 实例。
创建完成后,会把 ViewModel 实例存在 ViewModelStore 中。
这几层的分工结构可以用下图表示:
看到这里,我们比较关心的点剩下的就是如何使用 Factory 创建 ViewModel,以及 Factory 是怎么来的,有哪些 Factory。
我们看下默认情况下,ViewModelStoreOwner 是 FragmentActivity
或者 Activity
的时候,是怎么使用 Factory 的:
owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner)
.getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance()
// Activity
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
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 (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
// Fragment
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
requireActivity().getApplication(),
this,
getArguments());
}
return mDefaultFactory;
}
可以看到 FragmentActivity 和 Fragment 默认的 ViewModel.Factory 都是 SavedStateViewModelFactory
:
// SavedStateViewModelFactory#create
@Override
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
if (isAndroidViewModel) {
constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
} else {
constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
}
if (constructor == null) {
return mFactory.create(modelClass);
}
T viewmodel;
if (isAndroidViewModel) {
viewmodel = constructor.newInstance(mApplication, controller.getHandle());
} else {
viewmodel = constructor.newInstance(controller.getHandle());
}
viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
return viewmodel;
}
这里会去尝试匹配构造函数为
constructor(Application, SavedStateHandle)
constructor(SavedStateHandle)
这两种的 ViewModel
。
通过名字我们就能知道, SavedStateHandle
是用来处理进程销毁等重建情况的数据恢复的。
这部分在 Android 的官方文档里面可以看到:https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate?hl=zh-cn。
从这里我们也可以知道,ViewModel 之前在进程被系统终止之后其实是不会保存数据给 ViewModel 的,在添加了 SaveStateHandle
之后才支持。
如果需要,我们可以在自己的 ViewModel 里加上 SaveStateHandle
的参数:
class MyViewModel(handle: SaveStateHandle) : ViewModel() {}
当我们的 ViewModel 没有适配 SaveStateHandle
的时候,会继续使用默认的 Factory 去创建 ViewModel,这里默认的是 AndroidViewModelFactory
, 父类是 ViewModelProvider.NewInstanceFactory
。
前者会默认尝试创建 AndroidViewModel
。否则会调用 super 的 create 方法,创建一个普通的 ViewModel
。
一般我们使用 FragmentActivity
或者 Fragment
作为我们的 ViewModelStoreOwenr, 以 FragmentActivity
为例:
@Override
public ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
这里可以看到我们有一个 NonConfigureationInstance
对象,当这个对象存在 ViewModelStore 实例的时候,会直接使用这个 ViewModelStore。这个实例会在屏幕旋转、更改显示设置等情况下去保存我们的 ViewModelStore:
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
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;
}
这也就是为什么Android在发布架构组件之初就说明 ViewModel 不会受屏幕变化影响,始终保持原先实例的原因。
本文介绍的内容比较简单,但是也比较实用,这些内容里有一些点值得我们注意:
Activity 1.1.0
和 Fragment 1.2.0
之后,也支持进程回收等情况的数据恢复功能。有类似需求的时候可以通过定义 ViewModel
中加入 SavedStateHandle
参数来实现。