ViewModel的使用场景

Activity or Fragment为Controller

场景一、Controller配置变化导致其重建

若需要保证Controller转屏前后的数据一致性,传统的方法需要我们在onSaveInstance保存数据,复杂类型的数据还必须实现Parcelable接口。使用ViewModel去维护数据则可以解决这个问题,ViewModel为我们在重建的时候保存了数据。

场景二、多Controler共享数据

若要实现Activity和Fragment之间的做法,我们常用的方法有:SharedPreference、Database、全局变量、两者之间回调。这些实现会随着业务规模的扩大,变得难以维护,且不易独立进行测试。ViewModel独立于两者之间,只有两者共同维护一个ViewModel实例,就能轻松实现共享数据,而且ViewModel是与生命周期绑定的,不用去手动销毁。

ViewModel的原理

ViewModel的原理图

如图所示:

  • 所有已经实例化的ViewModel缓存在ViewModelStore中,其实质就是一个HashMap;
  • ViewModerStore与具体的Controller绑定,并与宿主 Controller 俱生俱灭,所以这就解释了为何 ViewModel 与宿主 Controller 的生命周期是一样长了,因为缓存它的 ViewModelStore 与宿主 Controller 寿命相等;
  • 获取ViewModel实例的方法委托给了ViewModerProvider, 若缓存中有则直接得到,若没有才创建并缓存;

源码分析

获取ViewModel的过程

1
2
3
4
5
6
7
8
9
10
11
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {  
//构造ViewModel工厂
mFactory = factory;
//FragmentActivity或者Fragment持有的ViewModelStore
mViewModelStore = store;
}

public class ViewModelStore {
//key其实就是ViewModel的类名
private final HashMap<String, ViewModel> mMap = new HashMap<>();
}

可以看到ViewModelProvider持有了ViewModelStore,而在ViewModelStore内部通过一个HashMap去维护ViewModel缓存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@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);
}

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//尝试从缓存中获取ViewModel
ViewModel viewModel = mViewModelStore.get(key);
//获取的viewModel不为null
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.
}
}
//为空通过工厂创建一个ViewModel
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
} else {
viewModel = mFactory.create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}

接下来看看工厂具体创建ViewModel的过程, 默认的工厂是SavedStateViewModelFactory。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public final class SavedStateViewModelFactory extends ViewModelProvider.KeyedFactory {  
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
Constructor<T> constructor;
//寻找合适的ViewModel构造方法
if (isAndroidViewModel && mApplication != null) {
constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
} else {
constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
}
// doesn't need SavedStateHandle
//不需要SavedStateHandle直接通过工厂构建
if (constructor == null) {
return mFactory.create(modelClass);
}

SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
//通过寻找的构造器构造
try {
T viewmodel;
if (isAndroidViewModel && mApplication != null) {
viewmodel = constructor.newInstance(mApplication, controller.getHandle());
} else {
viewmodel = constructor.newInstance(controller.getHandle());
}
viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
return viewmodel;
}
}
}

ViewModel与宿主生命周期保持一致的原理

前面我们说ViewModel与ViewModel与宿主生命周期保持一致,其中原理是什么。

ViewModelStore树