SavedState组件是用于保存和恢复数据,替换onSaveInstanceState
方法。我们的数据会因为Activity/Fragment被系统回收,屏幕旋转等导致丢失,除了常规的onSaveInstanceState
中去保存以外,我们可以使用savedstate
组件,他可以结合ViewModel,LiveData一起使用,比较的灵活简便。
使用
在Activity的ViewModel中,他是可以在屏幕旋转的时候自行恢复数据的,代码在ComponentActivity中,当屏幕旋转的时候会调用下面方法
1 | public final Object onRetainNonConfigurationInstance() { |
就是配置发生了更改,viewModelStore会被保留下来。我们从ensureViewModelStore()
方法判断ViewModelStore是否为空也可以看得出来,所以我们不需要处理这种情况。
对于系统资源限制导致Activity被销毁而言,假如是继承了HasDefaultViewModelProviderFactory
的Activity或者是Fragment,我们可以直接使用ViewModelProvider(@NonNull ViewModelStoreOwner owner)
构造方法获取ViewModelProvider对象即可,因为这类Activity/Fragment实现了getDefaultViewModelProviderFactory()
方法,提供了SavedStateViewModelFactory
实例的获取,他是SavedState组件的用于创建ViewModel的Factory。
对于需要使用SavedState组件的ViewModel的构造函数,它是有要求的:
- 要么是只有一个参数类型为
SavedStateHandle
- 要么是由两个参数,第一个参数是
Application
类型,第二个是SavedStateHandle
二者的的区别就类似ViewModelProvider中的NewInstanceFactory
和AndroidViewModelFactory
,例如
1 | class MyVM(val handle:SavedStateHandle) : ViewModel(){ |
然后创建ViewModel
1 | private var provider: ViewModelProvider? = null |
对于Activity而言,需要保存的数据是自身产生的数据,对于传递过来的Bundle则不需要另外通过SavedStateHandle进行保存而会自动的保存。可以看下面例子
1 | class TestVM(val handle: SavedStateHandle) : ViewModel() { |
关于MutableLiveData
这个后续再说,先知道他是一个可以观测生命周期的组件,用于异步通知而又不会造成内存泄漏。
流程分析
以我们的ComponentActivity为例子
首先,我们的ComponentActivity提供了SavedStateRegistryController对象的获取,该对象会在onCreate
和onSaveInstanceState
被调用
1 | public class ComponentActivity implement SavedStateRegistryOwner{ |
我们就先从数据如何保存开始看源码,当我们的Activity被系统杀死回收的时候,调用了performSave(outState)
1 | //SavedStateRegistryController的保存数据方法 |
可以看他调用了自己本身的变量mRegistry的performSave,mRegistry是SavedStateRegistry类对象,我们继续看下去
1 | //SavedStateRegistry的方法 |
经过这样就完成了数据保存,也就是把所有注册到SavedStateRegistry的SavedStateProvider返回的Bundle进行保存,同时假如存在上一次恢复的数据,那么也保存。
接下来我们看看数据的恢复操作
1 | //SavedStateRegistryController中的方法 |
然后我们看看Recreator在在观测到的生命周期干了什么
1 | //Recreator.java |
其中,Recreator的SavedStateProvider(即需要缓存那些类)的定义是
1 | static final class SavedStateProvider implements SavedStateRegistry.SavedStateProvider { |
在performRestore
中执行完了lifecycle.addObserver(new Recreator(mOwner));
之后,他会调用mRegistry.performRestore(lifecycle, savedState);
,我们看下他的具体处理逻辑
1 | @MainThread |
SavedStateRegistry还有一个比较重要的方法,就是数据恢复以后外部如何获取,它提供了一个consumeRestoredStateForKey
方法
1 | @MainThread |
上面就是一个大概的数据保存和恢复的过程,接下来我们看ViewModel如何利用这套实现数据的保存和恢复的,我们先看SavedStateViewModelFactory的create
方法,看看他如何创建的ViewModel
1 | public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) { |
然后我们看SavedStateHandleController的create
方法
1 | //SavedStateHandleController |
在SavedStateHandleController的attachToLifecycle
中,把SavedStateHandle的需要保存的数据注册到了SavedStateRegistry中
1 | void attachToLifecycle(SavedStateRegistry registry, Lifecycle lifecycle) { |
最后,我们看我们会在ViewModel中使用到的类SavedStateHandle
,他的作用是我们可以在ViewModel中取出数据,假如该数据被系统回收了,他能去到最后一次设置的值。他的成员变量有下面这些:
mRegular
,存储数据的键值对,包括设置进来的数据还是恢复回来的数据都会存入这个集合,同时该集合会在被系统回收的时候存入outBundle中mLiveDatas
,一个用于缓存LiveData的键值对,只是缓存内存数据mSavedStateProvider
,定义了mRegular在被系统回收的时候如何生成一个Bundle被保存下来
接下来看SavedStateHandle
的具体解析,他是通过自身的create
创建的对象
1 | static SavedStateHandle createHandle(@Nullable Bundle restoredState, |
从上面看出,在生成SavedStateHandle的同时也会把无论是默认数据还是恢复的数据存入mRegular
之中,其中被系统回收的时候,mRegular
被存入的方式是
1 | private final SavedStateProvider mSavedStateProvider = new SavedStateProvider() { |
上面就是SavedStateHandle的数据的存入和恢复的过程,他还会被ViewModel使用和写入的过程,我们看下他是如何做的
1 | MutableLiveData<T> getLiveData(@NonNull String key)// 他间距调用了getLiveDataInternal |
当然,假如我们不想要包裹着Value的LiveData,可以直接他的 <T> T get(String key)
方法,假如需要移除则使用他的<T> T remove(String key)
方法,判断是否存在则使用boolean contains(String key)
方法,更新数据则使用<T> void set(@NonNull String key, @Nullable T value)
到这里,与SavedState组件结合ViewModel的整个过程基本就结束了。
整体总结
其实经过上面的流程,我们可以发现,lifecycle-viewmodel-savedstate
组件和savedstate
组件是两个不同的作用。后者实现了一套无侵入式存储和恢复数据的方式,而前者则是利用这一套实现了ViewModel中的数据的恢复和存储。
经过上面的流程分析,我们也大概知道了整体的实现过程,我们先列举各个类的大概作用savedstate
组件的:
- SavedStateRegistryController,负责提供SavedStateRegistry对象和间接调用SavedStateRegistry的方法
- SavedStateRegistryOwner,负责外部获取SavedStateRegistry对象,同时,他自己也是继承了LifecycleOwner的
- SavedStateRegistry,负责数据的保存和恢复
- AutoRecreated,提供了一个接口调用他的
onRecreated(SavedStateRegistryOwner owner)
,他只会在生命周期的onCreate
被执行,OnRecreation
继承它用于判断SavedStateHandleController时候需要重新attached - SavedStateProvider,是SavedStateRegistry定义的内部接口,试下该接口并把该接口注入到SavedStateRegistry中即可实现数据保存和恢复
lifecycle-viewmodel-savedstate
组件的
- SaveStateViewModelFactory,定义生成ViewModel对象的规则,同时创建SavedStateHandleController对象
- SavedStateHandleController,负责管理SavedHandle对象
- SavedStateHandle,他负责与ViewModel进行交互,存储和恢复ViewModel中使用该Handle存取的数据
- OnRecreation,用于判断SavedStateHandleController是否需要重新attached,同时假如ViewModelProvider存在key数据则把他注入到可恢复数据中。
关于key值的问题:我们通过SavedStateProvider缓存的Bundle,它本身也是存储在Bundle中的,多个SavedStateProvider中的Bundle假如他们内部的Key一致,但是存入到总的Bundle的key不一致是没有冲突的。比如Recreator
中存入SavedStateRegistry的总Bundle,即mComponents
对象的key值是androidx.savedstate.Restarter
,其他存入总Bundle的单个Bundle,他的key值不是androidx.savedstate.Restarter
即可,它内部的key值不会与外部的冲突。
关于ViewModel的创建的的问题:SavedStateViewModelFactor是继承自KeyedFactory
的,它的create()
方法会多一个key
参数,key
的值是我们调用ViewModelProvider
的get()
方法传入的,不传则是DEFAULT_KEY + ":" + canonicalName
,同时,改key值是作为存入总Bundle的key值,标识着ViewModel被保存的Bundle。
OnRequeryFactory的问题,首先它是定义在ViewModelProvider中的一个接口,在调用ViewModelProvider的get方法获取ViewModel的时候,假如已经存在了该ViewModel对象同时如果该Factory是实现了OnRequeryFactory接口,则会调用它的onRequery方法。我们的SavedStateViewModelFactory就实现了OnRequeryFactory接口,在onRequery方法中,做了如下的处理
1 | void onRequery(@NonNull ViewModel viewModel) { |
他针对的情况是比如屏幕旋转,Activity的生命周期重新走了一次,ViewModel并没有被销毁,获取ViewModel的时候SavedStateViewModelFactory的create
方法不会被调用,只会执行onRequery,这时候我们需要把SavedStateHandleController重新attached到生命周期组件中,这就是他的作用。