目录
源码解析目录
本文需要先了解ViewModel的源码:源码解析之ViewModel
前言
ViewModel 2.x版本相较于ViewModel 1.x版本有两个显著的升级:1. 支持Kotlin协程 2.支持保存ViewModel的“状态”。本篇文章主要分析ViewModel状态保存的相关源码,来看看ViewModel-SavedState是如何帮助我们保存状态的。源码版本 androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0
。
使用场景
众所周知,ViewModel的核心功能是它不受配置(configuration)变化的影响,是跨配置保存数据的理想场所,也是我们业务逻辑理想的存放场所。
但是这里还存在一个问题,我们不仅仅需要跨配置保存数据,有时候我们还需要跨进程保存数据。跨进程保存数据不是指的跨进程数据传输(IPC),而是指应用因“意外”被销毁之后,重新进入应用的时候,希望能恢复到之前离开应用时的状态。众所周知,Android系统会在需要内存的时候,干掉一些后台的应用以释放更多内存,而这些被干掉的应用的Activity会给你一个机会onSaveInstanceState()
保存你当下的状态,待到你恢复的时候再通过onCreate()
,onRestoreInstanceState()
给你机会恢复。即使我们什么都不做,系统也会默认帮你保存很多东西,像是EditText的输入文本,光标位置等等之类的View显示状态都会被默认保存下来。但这往往还不足够,要想记录更多状态就需要我们自己去实现onSaveInstanceState()
,onCreate()
,onRestoreInstanceState()
这些方法。只是有个限制,不能通过这种方式保存很大的数据,只能保存小型的可序列化的数据。大数据的保存要通过文件、数据库等持久化存储的方式。更多内容可以查看Android官方文档保存界面状态。
综上所述,业务逻辑我们放在了ViewModel中,配置变化已经影响不到我们的逻辑数据了✅。是如果我们想要跨进程保存一些数据以便界面恢复时使用,这就有点麻烦了❎。我们需要在Activity中去onSaveInstanceState()
,然后恢复的时候通过onCreate()
或者onRestoreInstanceState()
拿回原来的数据再传递给我们的ViewModel,这的确有点麻烦,最理想的方式就是,跨进程数据保存恢复也在ViewModel中完成,而ViewModel 2.x就实现了这个功能,主要就是由lifecycle-viewmodel-savedstate这个库实现的。所以ViewModel现在跨配置✅,跨进程✅。
更多viewmodel-savedstate的具体使用方法可以查看Android的官方文档ViewModel 的保存状态模块。
1. 太长不看
一句话总结,每个使用SavedState的ViewModel都会创建一个Bundle来保存“状态”数据,最后这些Bundle会被汇总到一个Bundle中,然后被保存到onSaveInstanceState(Bundle outState)
的outState
中;当恢复的时候,会从onCreate(Bundle savedInstanceState)
中的savedInstanceState
中取出原来存放的总Bundle,然后再取出一个个的属于ViewModel的分Bundle,于是我们就能通过封装好的SavedStateHandle
来方便地存取“状态”了。其实就是利用Bundle可以保存另一个Bundle这么一个特点,分层分区保存数据,让数据之间相互分离,进而方便存取。
2. SavedState的主要类
- SavedStateRegistry
- SavedStateRegistryController
- SavedStateHandle
- SavedStateHandleController
- Recreator
- OnRecreation
是不是很晕,没错,我刚开始看也很晕,这名字起得简直就是张三、张三三、张珊、张珊珊。总之呢,核心功能在SavedStateRegistry
和SavedStateHandle
两个类中,Controller类就是对应类的控制类。SavedStateHandle
是我们能接触到类,功能就是你使用起来的那个样子;SavedStateHandleController
主要是帮你把状态保存到SavedStateRegistry
;而SavedStateRegistry
主要功能就是上文提到的把一个个ViewModel的Bundle汇总到一个Bundle中,当然还有反方向功能,从总Bundle中取出一个个属于ViewModel的Bundle;SavedStateRegistryController
的主要功能是与Activity、Fragment建立联系,在onSaveInstanceState
,onCreate
时保存、恢复“总Bundle”。至于Recreator
和OnRecreation
的功能我们之后再看。
3. SavedStateViewModelFactory
要想使用ViewModel的SavedState功能,我们只需要给ViewModel增加一个参数SavedStateHandle:MyViewModel(handle: SavedStateHandle) : ViewModel()
,这样就OK了,就这么简单。那么问题就简单了,只需要知道SavedStateHandle
是怎么来的就可以了。想想也只有一种可能性,必然是通过某个ViewModelFactory。我们知道每个ViewModel的创建都是通过ViewModelFactory来实现的,即使我们不在获取ViewModel时候指定,也会有一个默认的ViewModelFactory,这在之前是AndroidViewModelFactory
,而现在是SavedStateViewModelFactory
,SavedStateViewModelFactory
与AndroidViewModelFactory
最大的不同就是它提供了SavedStateHandle
给我们的ViewModel:
public final class SavedStateViewModelFactory extends ViewModelProvider.KeyedFactory {
public SavedStateViewModelFactory(@NonNull Application application,
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs) {
//通过 SavedStateRegistryOwner获取 SavedStateRegistry
mSavedStateRegistry = owner.getSavedStateRegistry();
mLifecycle = owner.getLifecycle();
mDefaultArgs = defaultArgs;
mApplication = application;
//还是原来的 AndroidViewModelFactory
mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
@NonNull
@Override
public T create(@NonNull String key, @NonNull Class modelClass) {
//通过 modelClass 判断一下这个 ViewModel是否需要 SavedStateHandle,如果不需要,那么使用 AndroidViewModelFactory 直接返回
//创建 SavedStateHandleController
SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
try {
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;
} catch ...
}
}
关键点有两处,一是SavedStateViewModelFactory
通过SavedStateRegistryOwner
获取了SavedStateRegistry
,二是通过SavedStateHandleController.create
这个简单工厂方法构造出来了SavedStateHandleController
。
所以说,SavedStateViewModelFactory
会创建SavedStateHandleController
,调用其getHandle()
方法就可以获得SavedStateHandle
,于是包含SavedStateHandle
参数的ViewModel就被创建了出来。我们只需要在获取ViewModel的时候提供SavedStateViewModelFactory
就可以了。其实呢,也不用我们提供,因为Activity、Fragment默认就是使用的SavedStateViewModelFactory
:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
HasDefaultViewModelProviderFactory {
//实现 HasDefaultViewModelProviderFactory接口
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
//...
if (mDefaultFactory == null) {
// SavedStateViewModelFactory 就是默认的 ViewModelFactory
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
}
Fragment也是类似的。我们当然可以覆盖getDefaultViewModelProviderFactory
方法来提供自己的ViewModelFactory,事实上除了SavedStateViewModelFactory
,还有一个AbstractSavedStateViewModelFactory
,我们如果想实现自己的ViewModelFactory,同时还想保留SavedStateHandle
的话,可以继承AbstractSavedStateViewModelFactory
。
4. SavedStateRegistry和SavedStateRegistryController
就像上面看到的那样,创建SavedStateHandleController
需要SavedStateRegistry
,而SavedStateRegistry
又是SavedStateRegistryController
创建的,SavedStateRegistryController
呢,它是Activity、Fragment的一个成员变量:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
SavedStateRegistryOwner {
private final SavedStateRegistryController mSavedStateRegistryController =
SavedStateRegistryController.create(this);
//实现 SavedStateRegistryOwner接口
@NonNull
@Override
public final SavedStateRegistry getSavedStateRegistry() {
return mSavedStateRegistryController.getSavedStateRegistry();
}
}
public final class SavedStateRegistryController {
private final SavedStateRegistryOwner mOwner;
private final SavedStateRegistry mRegistry;
@NonNull
public static SavedStateRegistryController create(@NonNull SavedStateRegistryOwner owner) {
return new SavedStateRegistryController(owner);
}
private SavedStateRegistryController(SavedStateRegistryOwner owner) {
mOwner = owner;
//新建一个 SavedStateRegistry
mRegistry = new SavedStateRegistry();
}
@NonNull
public SavedStateRegistry getSavedStateRegistry() {
return mRegistry;
}
}
SavedStateRegistryController本身是很简单的,只是SavedStateRegistry的一个壳儿而已,它的主要目的就是收缩SavedStateRegistry的方法,因为很多SavedStateRegistry的方法在Activity、Fragment中是用不到的,所以用SavedStateRegistryController来隔离一下,这叫啥,这就叫单一职责、接口隔离原则,用设计模式的话讲,这叫外观模式。
Activity、Fragment其实只会用到SavedStateRegistry的两个方法,所以SavedStateRegistryController就暴露了这两个方法:
//SavedStateRegistryOwner 也是一个 LifecycleOwner
public interface SavedStateRegistryOwner extends LifecycleOwner {
@NonNull
SavedStateRegistry getSavedStateRegistry();
}
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
SavedStateRegistryOwner {
private final SavedStateRegistryController mSavedStateRegistryController =
SavedStateRegistryController.create(this);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//从 savedInstanceState中恢复
mSavedStateRegistryController.performRestore(savedInstanceState);
}
@CallSuper
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
//把想保存的保存在 outState中
mSavedStateRegistryController.performSave(outState);
}
}
public final class SavedStateRegistryController {
private final SavedStateRegistryOwner mOwner;
private final SavedStateRegistry mRegistry;
@MainThread
public void performRestore(@Nullable Bundle savedState) {
Lifecycle lifecycle = mOwner.getLifecycle();
//验证一下调用的时机
if (lifecycle.getCurrentState() != Lifecycle.State.INITIALIZED) {
throw new IllegalStateException("Restarter must be created only during "
+ "owner's initialization stage");
}
//...
mRegistry.performRestore(lifecycle, savedState);
}
@MainThread
public void performSave(@NonNull Bundle outBundle) {
mRegistry.performSave(outBundle);
}
}
恢复该恢复的,保存想保存的,这一切的核心其实都在SavedStateRegistry中。
public final class SavedStateRegistry {
public interface SavedStateProvider {
//想通过 SavedStateRegistry保存状态就得实现这个方法
//把你想保存的状态一起打包成一个Bundle,返回即可
@NonNull
Bundle saveState();
}
//这是要保存的数据集合
private SafeIterableMap mComponents =
new SafeIterableMap<>();
@Nullable
private Bundle mRestoredState;
private boolean mRestored;
@MainThread
void performRestore(@NonNull Lifecycle lifecycle, @Nullable Bundle savedState) {
//只能恢复一次
if (mRestored) {
throw new IllegalStateException("SavedStateRegistry was already restored.");
}
if (savedState != null) {
//这里的 savedState就是 Activity中的 savedInstanceState,为 null证明没啥好恢复的
mRestoredState = savedState.getBundle(SAVED_COMPONENTS_KEY);
}
//...
mRestored = true;
}
@MainThread
void performSave(@NonNull Bundle outBundle) {
Bundle components = new Bundle();
if (mRestoredState != null) {
components.putAll(mRestoredState);
}
//遍历要保存的数据集合
for (Iterator> it =
mComponents.iteratorWithAdditions(); it.hasNext(); ) {
Map.Entry entry1 = it.next();
components.putBundle(entry1.getKey(), entry1.getValue().saveState());
}
//一切被汇总到了一个Bundle中 components
outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
}
}
有想要保存的数据就需要添加到SafeIterableMap
中,来看看如何想其中添加:
public final class SavedStateRegistry {
//取出之前保存的数据
@MainThread
@Nullable
public Bundle consumeRestoredStateForKey(@NonNull String key) {
if (!mRestored) {
throw new IllegalStateException("You can consumeRestoredStateForKey "
+ "only after super.onCreate of corresponding component");
}
if (mRestoredState != null) {
Bundle result = mRestoredState.getBundle(key);
mRestoredState.remove(key);
if (mRestoredState.isEmpty()) {
mRestoredState = null;
}
return result;
}
return null;
}
//注册想要保存的数据,并不是即刻保存,而是提供一个 SavedStateProvider,等到 performSave再保存
@MainThread
public void registerSavedStateProvider(@NonNull String key,
@NonNull SavedStateProvider provider) {
SavedStateProvider previous = mComponents.putIfAbsent(key, provider);
if (previous != null) {
throw new IllegalArgumentException("SavedStateProvider with the given key is"
+ " already registered");
}
}
@MainThread
public void unregisterSavedStateProvider(@NonNull String key) {
mComponents.remove(key);
}
}
所以通过SavedStateRegistry存数据是很简单的,实现SavedStateProvider
接口,注册一下就可以了;取回数据时,通过原来的key取回即可。SavedStateRegistry的核心目的是分别保存Bundle数据,这些Bundle多数是一个个ViewModel保存的状态数据,每个ViewModel一个Bundle,最后合在一起组成一个components
Bundle,以SAVED_COMPONENTS_KEY
作为key保存进了Activity的onSaveInstanceState(Bundle outState)
的outState
中。恢复的时候以逆过程恢复即可,条理清晰,层次分明。
5. SavedStateHandle和SavedStateHandleController
SavedStateRegistry已经帮我们铺好路了,现在只需要在SavedStateHandle收集起来想保存的状态,实现SavedStateProvider
接口,然后调用registerSavedStateProvider
方法即可;对于恢复而言,就需要通过consumeRestoredStateForKey
获得对应的Bundle,然后取出之前保存的状态。SavedStateHandle和SavedStateHandleController的分工也很明确,SavedStateHandleController负责与SavedStateRegistry沟通,调用registerSavedStateProvider
和consumeRestoredStateForKey
存取属于ViewModel的Bundle数据;而SavedStateHandle拿到这个Bundle数据之后,再从中取出状态数据,并且实现SavedStateProvider
接口,把状态数据打包成一个Bundle传给SavedStateHandleController。也就是SavedStateHandleController负责粗粒度的Bundle块数据,SavedStateHandle负责细粒度的状态数据。
先来看一下SavedStateHandleController:
final class SavedStateHandleController implements LifecycleEventObserver {
//从SavedStateRegistry取 Bundle时需要的 key,其实就是ViewModel的 key
private final String mKey;
private boolean mIsAttached = false;
private final SavedStateHandle mHandle;
SavedStateHandleController(String key, SavedStateHandle handle) {
mKey = key;
mHandle = handle;
}
//简单工厂方法
static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
String key, Bundle defaultArgs) {
//从SavedStateRegistry取出相应的Bundle
Bundle restoredState = registry.consumeRestoredStateForKey(key);
//上面取出的Bundle加上默认的参数传递给SavedStateHandle
SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
//创建自己
SavedStateHandleController controller = new SavedStateHandleController(key, handle);
//主要就是为了调用SavedStateRegistry的registerSavedStateProvider
controller.attachToLifecycle(registry, lifecycle);
tryToAddRecreator(registry, lifecycle);
return controller;
}
}
再来回顾一下,SavedStateHandleController.create
方法是在SavedStateViewModelFactory
中被调用的:
public final class SavedStateViewModelFactory extends ViewModelProvider.KeyedFactory {
@NonNull
@Override
public T create(@NonNull String key, @NonNull Class modelClass) {
//创建 SavedStateHandleController
SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
}
}
所以呢,SavedStateHandleController中的key就是ViewModel的key;defaultArgs对于Activity而言就是getIntent().getExtras()
,对于Fragment而言就是getArguments()
。
我们已经看到了SavedStateHandleController会在create
工厂方法里调用registry.consumeRestoredStateForKey
,那么就一定还会调用registry.registerSavedStateProvider
,这是一对逆过程。
final class SavedStateHandleController implements LifecycleEventObserver {
static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
String key, Bundle defaultArgs) {
//...
controller.attachToLifecycle(registry, lifecycle);
return controller;
}
void attachToLifecycle(SavedStateRegistry registry, Lifecycle lifecycle) {
//...
//最核心的就是这里,SavedStateHandle负责打包成一个Bundle,然后注册进SavedStateRegistry
registry.registerSavedStateProvider(mKey, mHandle.savedStateProvider());
}
}
也就是说SavedStateHandleController会负责与SavedStateRegistry沟通,从中恢复、保存Bundle块数据,至于再从Bundle块数据中恢复状态数据,并且把状态数据打包成一个Bundle块数据,那就是SavedStateHandle的事了。SavedStateHandle的源码比较简单,基本上就是你平常使用的那个样子,这里就不再分析了。
6. Recreator和OnRecreation
其实整个流程到这里基本上已经结束了,数据一层层的从Activity/Fragment逐步拆分直到SavedStateHandle,还有数据从SavedStateHandle逐步合并最后保存到SaveInstanceState中。但是这里面有个bug,SavedStateRegistry和SavedStateRegistryController都是与生命周期绑定的,每次新建Activity/Fragment时,它们都会随之新建,但是ViewModel却不受配置变化的影响,如果配置发生了变化,SavedStateRegistry和SavedStateRegistryController会新建一个实例,但是ViewModel不会,新建的SavedStateRegistry不会包含之前registerSavedStateProvider
注册的信息。
这样的话,在配置发生变化而导致Activity重建这种情形下,新的SavedStateRegistry就会是个空壳。所以必须在Activity重建时,把ViewModel代表的SavedStateProvider重新注册进新的SavedStateRegistry。这就是Recreator和OnRecreation的作用。
先来看一个小细节:
public final class SavedStateViewModelFactory extends ViewModelProvider.KeyedFactory {
@NonNull
@Override
public T create(@NonNull String key, @NonNull Class modelClass) {
SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
//在新建viewmodel时,与之匹配的 SavedStateHandleController会被保存下来
viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
}
}
也就是说,SavedStateHandleController是保存在ViewModel内部的,不会随配置变化而新建,我们只需要在恰当的时机,重新调用SavedStateHandleController的attachToLifecycle
就会再次向SavedStateRegistry注册registerSavedStateProvider
,这就是OnRecreation的作用:
final class SavedStateHandleController {
static final class OnRecreation implements SavedStateRegistry.AutoRecreated {
//这个方法会被Recreator调用,之后会看到
@Override
public void onRecreated(@NonNull SavedStateRegistryOwner owner) {
//这里的owner其实就是Activity/Fragment本身
if (!(owner instanceof ViewModelStoreOwner)) {
throw new IllegalStateException();
}
ViewModelStore viewModelStore = ((ViewModelStoreOwner) owner).getViewModelStore();
//这个SavedStateRegistry就是新的 SavedStateRegistry
SavedStateRegistry savedStateRegistry = owner.getSavedStateRegistry();
//遍历所有的ViewModel
for (String key : viewModelStore.keys()) {
ViewModel viewModel = viewModelStore.get(key);
//定义在下方
attachHandleIfNeeded(viewModel, savedStateRegistry, owner.getLifecycle());
}
if (!viewModelStore.keys().isEmpty()) {
savedStateRegistry.runOnNextRecreation(OnRecreation.class);
}
}
}
static void attachHandleIfNeeded(ViewModel viewModel, SavedStateRegistry registry,
Lifecycle lifecycle) {
//从ViewModel再取出之前保存的 SavedStateHandleController
SavedStateHandleController controller = viewModel.getTag(
TAG_SAVED_STATE_HANDLE_CONTROLLER);
if (controller != null && !controller.isAttached()) {
//又回到了这个方法,registerSavedStateProvider会被调用
controller.attachToLifecycle(registry, lifecycle);
tryToAddRecreator(registry, lifecycle);
}
}
}
就像源码展示的那样,OnRecreation的作用就是重新让每个ViewModel再次调用registerSavedStateProvider
。那OnRecreation的onRecreated
方法又是如何别调用的呢?这就是Recreator的作用了:
final class Recreator implements GenericLifecycleObserver {
private final SavedStateRegistryOwner mOwner;
Recreator(SavedStateRegistryOwner owner) {
mOwner = owner;
}
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
//也就是说必然是在 onCreate的时候调用
if (event != Lifecycle.Event.ON_CREATE) {
throw new AssertionError("Next event must be ON_CREATE");
}
source.getLifecycle().removeObserver(this);
//取出之前保存的数据
Bundle bundle = mOwner.getSavedStateRegistry()
.consumeRestoredStateForKey(COMPONENT_KEY);
//这个保存的数据其实就是个字符串数组,每个字符串都是个类名
ArrayList classes = bundle.getStringArrayList(CLASSES_KEY);
for (String className : classes) {
//反射构造出 OnRecreation,然后调用它的 onRecreated方法
reflectiveNew(className);
}
}
}
Recreator会在ON_CREATE的时候反射构造出OnRecreation,然后调用它的onRecreated
方法,流程大致是这样的:
SavedStateRegistry只能保存可序列化的数据,所以想要保存OnRecreation类,就只能保存其类名,恢复的时候再反射构建。至于SavedStateHandleController是如何保存OnRecreation到SavedStateRegistry,Recreator又是何时被创建的,这些都比较细节,这里就不再分析了。总之,Recreator和OnRecreation一起保证了,在Activity因配置变化而重建的时候,ViewModel仍然会被注册进SavedStateRegistry,不影响后续保存状态数据。
7. 总结
- SavedStateRegistry:保存/恢复块Bundle数据
- SavedStateRegistryController: 收缩SavedStateRegistry接口,给Activity/Fragment调用
- SavedStateHandle:ViewModel用来保存/恢复状态数据
- SavedStateHandleController:保存/恢复属于ViewModel的Bundle
- Recreator:在onCreate的时候调用OnRecreation
- OnRecreation:ViewModel重新注册进SavedStateRegistry
图中没有展示Recreator和OnRecreation的流程。从上图也可以看出,SavedStateRegistry处于中心的位置,通过SavedStateRegistry,我们可以轻松地保存和恢复数据,不再强依赖于Activity/Fragment;而SavedStateHandle只是对SavedStateRegistry的使用而已,即使不是ViewModel-SavedState也可以使用SavedStateRegistry来方便地保存/恢复数据。