ViewModel是Android构架组件(Android Architecture Components)的一部分,我们从ViewModel的创建开始,一步步探索ViewModel的内部实现原理,以及为何它在configuration改变(常见的是横竖屏切换的场景)时,即使Activity已经重建,它仍然会保留。
ViewModel设计的宗旨就是根据生命周期来存储和管理界面相关的数据,减少开发维护成本,它是目前流的Android应用架构模式MVVM(Model-View-ViewModel)重要的组成部分,使用ViewModel的优势包括以下几个方面:
下面通过一个示例项目来解释,ViewModel在configuration改变时是如何被保留的。示例项目大概的功能是这样的,在MainActivity中展示一个计数和当前Activity的哈希值,这个计数通过LiveData保存在ViewModel中。当configuration改变时,当前的Activity实例将会销毁,通过它的哈希值我们能够看出来,但是保存在ViewModel中的计数值不会改变,这就说明ViewModel并没有销毁重新创建。
文件 | 大小 | 修改时间 |
---|---|---|
app | 2022年02月11日 | |
build.gradle | 662 B | 2022年02月11日 |
gradle/wrapper | 2022年02月11日 | |
gradle.properties | 1 kB | 2022年02月11日 |
gradlew | 5 kB | 2022年02月11日 |
gradlew.bat | 2 kB | 2022年02月11日 |
local.properties | 345 B | 2022年02月11日 |
README.md | 65 B | 2022年02月11日 |
settings.gradle | 47 B | 2022年02月11日 |
为什么Activity实例都销毁了,ViewModel实例却还保留?ViewModel创建之后保存在哪里了?在Activity中,创建一个ViewModelProvider实例,然后调用它的get方法就能得到相应的ViewModel实例。
viewModel = ViewModelProvider(this, ViewModelFactory()).get(CounterViewModel::class.java)
下面看看ViewModelProvider的构造方法,有两个参数,一个是ViewModelStoreOwner,用于提供ViewModelStore,我们的MainActivity的父类ComponentActivity实现了ViewModelStoreOwner接口,第二个是Factory,这个参数主要是用来自定义创建ViewModel1,最终会调用它另外一个重载的构造方法,把ViewModelStore和Factory保存下来。
1public interface ViewModelStoreOwner {
2 ViewModelStore getViewModelStore();
3}
4
5public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
6 this(owner.getViewModelStore(), factory);
7}
8
9public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
10 mFactory = factory;
11 mViewModelStore = store;
12}
接下来看看ViewModelProvider的get方法,通过get方法,我们才能获取到ViewModel实例。
1public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
2 String canonicalName = modelClass.getCanonicalName();
3 if (canonicalName == null) {
4 throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
5 }
6 return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
7}
8
9public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
10 ViewModel viewModel = mViewModelStore.get(key);
11 if (modelClass.isInstance(viewModel)) {
12 if (mFactory instanceof OnRequeryFactory) {
13 ((OnRequeryFactory) mFactory).onRequery(viewModel);
14 }
15 return (T) viewModel;
16 } else {
17 //noinspection StatementWithEmptyBody
18 if (viewModel != null) {
19 // TODO: log a warning.
20 }
21 }
22 if (mFactory instanceof KeyedFactory) {
23 viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
24 } else {
25 viewModel = (mFactory).create(modelClass);
26 }
27 mViewModelStore.put(key, viewModel);
28 return (T) viewModel;
29}
首先它会将ViewModel的类名和DEFAULT_KEY组合为key,然后调用重载的get方法,检查当前key在mViewModelStore2中是否存在,如果存在则直接返回,如果不存在,则调用Factory的create方法来创建ViewModel,然后保存在mViewModelStore中。
那么问题是ViewModelStore明明是Activity提供的,为什么Activity实例都销毁重建了,我们还能拿到之前创建的ViewModel呢?下面看看ComponentActivity中是如何实现ViewModelStoreOwner接口的。
1public class ComponentActivity extends androidx.core.app.ComponentActivity implements
2 LifecycleOwner,
3 ViewModelStoreOwner,
4 SavedStateRegistryOwner,
5 OnBackPressedDispatcherOwner {
6 ...
7 static final class NonConfigurationInstances {
8 Object custom;
9 ViewModelStore viewModelStore;
10 }
11
12 @Override
13 public ViewModelStore getViewModelStore() {
14 if (getApplication() == null) {
15 throw new IllegalStateException("Your activity is not yet attached to the "
16 + "Application instance. You can't request ViewModel before onCreate call.");
17 }
18 if (mViewModelStore == null) {
19 NonConfigurationInstances nc =
20 (NonConfigurationInstances) getLastNonConfigurationInstance();
21 if (nc != null) {
22 // Restore the ViewModelStore from NonConfigurationInstances
23 mViewModelStore = nc.viewModelStore;
24 }
25 if (mViewModelStore == null) {
26 mViewModelStore = new ViewModelStore();
27 }
28 }
29 return mViewModelStore;
30 }
31 ...
32}
从上面可以看出来,在Activity重新创建后,调用getViewModelStore方法时,会首先检查NonConfigurationInstances是否为空,不为空则从NonConfigurationInstances拿到ViewModelStore,而这个ViewModelStore就是Activity重建之前保存进去的。
在Activity销毁时,会调用AcitivityThread.performDestroyActivity方法,在这个方法中会调用Activity的retainNonConfigurationInstances方法来获取Activity.NonConfigurationInstances实例,并保存在ActivityClientRecord中。
public final class ActivityThread extends ClientTransactionHandler {
...
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
...
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
}
...
}
在Activity.retainNonConfigurationInstances方法中会调用ComponentActivity.onRetainNonConfigurationInstance来获取ComponentActivity.NonConfigurationInstances实例。
1public class Activity extends ... {
2 ...
3 NonConfigurationInstances retainNonConfigurationInstances() {
4 Object activity = onRetainNonConfigurationInstance();
5 ...
6 NonConfigurationInstances nci = new NonConfigurationInstances();
7 nci.activity = activity;
8 ...
9 return nci;
10 }
11 ...
12}
在ComponentActivity.onRetainNonConfigurationInstance方法中,会保存把当前的ViewModelStore保存下来。
1public class ComponentActivity extends ... {
2 ...
3 public final Object onRetainNonConfigurationInstance() {
4 Object custom = onRetainCustomNonConfigurationInstance();
5 ViewModelStore viewModelStore = mViewModelStore;
6 if (viewModelStore == null) {
7 NonConfigurationInstances nc =
8 (NonConfigurationInstances) getLastNonConfigurationInstance();
9 if (nc != null) {
10 viewModelStore = nc.viewModelStore;
11 }
12 }
13 if (viewModelStore == null && custom == null) {
14 return null;
15 }
16 NonConfigurationInstances nci = new NonConfigurationInstances();
17 nci.custom = custom;
18 nci.viewModelStore = viewModelStore;
19 return nci;
20 }
21 ...
22}
最后,在重新创建Activity时,会调用AcitivityThread.performLaunchActivity再把ActivityClientRecord作为参数传回来,这样新的Activity中就有了之前Activity的NonConfigurationInstances实例了。
注:Activity和ComponentActivity内部都定义来一个名为NonConfigurationInstances的静态内部类。
注释