安卓SavedStateRegistry类和ViewModel数据存储恢复机制

400 阅读9分钟

一、SavedStateRegistry

1、基础类介绍

① SavedStateProvider

这是一个接口类

image.png

意思就是数据提供者,需要实现saveState()方法,将要保存的数据存入Bundle返回,保存数据时会调用这个方法。

②SavedStateRegistry

保存数据的注册表,它本身不保存数据,只是记录谁需要保存数据和数据恢复时暂存恢复的数据,然后提供统一保存和恢复的方法。

从它维护的属性来看

image.png

首先用map存储SavedStateProvider对象,以此来记录谁需要保存数据,其次用restoredState来暂存恢复的数据。

再来看它的几个核心方法:

registerSavedStateProvider:进行注册操作

image.png

performSave:执行数据存储

image.png

可以看到,它首先将暂存的上次恢复的数据存了进去,然后遍历map,拿它们要保存的数据并存入

这里有个点需要注意,map的value存储的是SavedStateProvider对象,key是注册时传进来的字符串,在这个方法里,key也变成了存储数据的key,也就是注册和存储的key是一致的

performRestore:执行数据恢复

image.png

可以看到它将数据取出后存入维护的属性restoredState

另外,调用registerSavedStateProvider方法时传入了key,用key来标记唯一的SavedStateProvider对象,因此在取出相对应的数据时,也需要传入key

consumeRestoredStateForKey:通过key取出存储的相关数据

image.png

方法名字里有个consume,代表消耗,所以具体操作就是把数据拿出来并删除

③SavedStateRegistryController

SavedStateRegistry的构造方法使用internal修饰,我们无法直接使用,但安卓提供了SavedStateRegistryController类,去执行上述操作。

它同样拥有performSave和performRestore方法,然后在里面调用SavedStateRegistry对应的方法。

但这些只是提供了一套接口,具体存储操作是在什么时候,恢复又在什么时候?

我们知道,Activity的方法onSaveInstanceState执行在Activity即将被销毁且系统有可能在稍后重建它时,因此最适合在这个时候保存数据:

image.png

将数据存入Bundle参数中。

与之对应的是onCreate方法,它也有个Bundle参数,从中可以将保存的数据取出,因此方便在这个时候恢复数据:

image.png

因此SavedStateRegistry其实还是借助的安卓老套数据恢复机制来实现的一些高级用法。

二、ViewModel数据存储恢复机制

上面介绍了一套数据存储恢复工具,那么安卓官方拿它做了具体什么应用呢?ViewModel就是其中之一。

1、ViewModelStore复用

说起ViewModel,听到最多的就是它生命周期比Activity长,它可以防止Activity非主动销毁而造成的数据丢失,它是如何实现的呢?

首先,我们知道ViewModel实例存储在ViewModelStore里(具体可从这里了解),但ViewModelStore并不是在Activity里直接实例化的一个对象,而是当Activity非主动销毁时存储起来

image.png

然后在用的时候,拿出上次存储的对象

image.png

这样实现了ViewModelStore的复用,在Activity销毁时存储,在重建后使用时恢复。

Activity重建时复用了ViewModelStore,因此它里面存储的ViewModel对象也没有被销毁,因此在这一点上可以理解ViewModel的数据存储恢复机制

2、使用SavedStateRegistry存储和恢复数据

除了上一点分析的ViewModelStore复用,似乎还有一个更高级的用法来实现数据存储和恢复,使用SavedStateHandle

官方是这么说的:

image.png

官方给出的具体用法是将SavedStateHandle作为参数,ViewModel在创建时会自动传入SavedStateHandle对象

image.png

①SavedStateHandle

SavedStateHandle与前面介绍的SavedStateRegistry的功能其实是类似的,也是记录需要存储的数据源,然后被通知存储时将数据存入Bundle对象并返回,虽然类似但它们不是平行关系,SavedStateHandle的功能实现需要依托SavedStateRegistry

首先来看它的维护的属性:

image.png

可以看出,它通过键值对存储LiveData、StateFlow以及其他对象

此外,它既通过匿名类实现了SavedStateProvider接口,又通过键值对存储实现了SavedStateProvider接口的对象,它是要干嘛呢?

其实,savedStateProviders是提供给用户使用的,它提供了一个对应的set方法:

image.png

官方也给出了示例:

image.png

这段代码的意思就是将ViewModel里的tempFile对象进行存储

通过匿名类实现SavedStateProvider接口的对象saveStateProvider,其实是利用它方便与SavedStateRegistry进行联动,来看它实现的saveState方法,可以看到它将key和value分别存入Bundle中并返回

image.png

那么它与SavedStateRegistry进行联动的方式有哪些呢?有两种

②SavedStateHandle的创建与存储

第一种通过SavedStateHandlesProvider

SavedStateHandlesProvider也实现了SavedStateProvider接口,并且拥有SavedStateHandlesVM的对象,SavedStateHandlesVM是ViewModel的子类,通过map存储着SavedStateHandle对象。

image.png

通过SavedStateHandlesProvider与SavedStateRegistry进行联动的过程如下:

1、注册

将SavedStateHandlesProvider对象注册到SavedStateRegistry中:

image.png

2、监测生命周期

我们前面知道,SavedStateRegistry会在onCreate的时候执行恢复,因此监测onCreate生命周期,将数据恢复:

image.png

具体的恢复就是调用SavedStateRegistry的consumeRestoredStateForKey方法,注意这里的key与注册时的key一致,将得到的数据暂存:

image.png

3、执行数据存储

SavedStateHandlesProvider对象被注册到SavedStateRegistry中后,SavedStateRegistry进行数据存储时就能调用它的saveState方法,得到它要保存的数据:

image.png

可以看到,遍历viewModel的map,调用SavedStateHandle的savedStateProvider()方法来得到它的saveStateProvider​属性,继续调用该属性的saveState方法得到要保存的数据

由此就联动起来了

4、创建和复用SavedStateHandle对象

创建ViewModel的过程不了解的具体可从这里了解

我们知道在Acitivity如果创建带SavedStateHandle参数的ViewModel对象,会运行这里:

image.png

来看createSavedStateHandle的具体实现:

image.png

provider就是前面注册的SavedStateHandlesProvider,viewModel就是前面提到的SavedStateHandlesVM,这个viewModel用map存储SavedStateHandle对象

所以首先,它会看map中有没有存储,没有就重新创建,创建的过程中,还会查看有没有被保存的相关的数据,如果有在创建SavedStateHandle时一并存入

image.png

这里可以看到,在SavedStateHandle中保存的key和value又回来了

然后调用ViewModel带有SavedStateHandle参数的构造函数实例化ViewModel

总结:

SavedStateHandlesProvider作为中间类,往下通过一个ViewModel存储着其他各ViewModel对应的SavedStateHandle对象,往上又链接着SavedStateRegistry。

当SavedStateRegistry通知需要存储数据时,会调用SavedStateHandlesProvider的saveState方法,而SavedStateHandlesProvider又调用SavedStateHandle相关的存储方法,SavedStateHandle又存储着ViewMoel需要存储的数据,它会将这些数据返回,通过这三级实现了ViewModel数据的存储。

SavedStateHandlesProvider注册了生命周期监听,当生命周期为onCreate时执行数据恢复,SavedStateHandlesProvider从SavedStateRegistry取出数据暂存,当ViewModel需要创建时,判断对应的SavedStateHandle是否已经创建,如果已经创建直接返回,如果没有创建就创建并存入存储的数据。

第二种通过SavedStateHandleController

上一种方法中,有中介SavedStateHandlesProvider,这个方法并没有中介,数据直接与SavedStateRegistry相连

我们还是按之前那个顺序来分析

1、注册

在SavedStateHandleController的attachToLifecycle方法里可以看到

image.png

它直接把SavedStateHandle的savedStateProvider注册到SavedStateRegistry中去了

2、监测生命周期

它不需要监测onCreate方法,因为它是直接与SavedStateRegistry绑定的,SavedStateRegistry会自动恢复数据,而它想要拿恢复的数据直接通过SavedStateRegistry拿就好

3、执行数据保存

被注册到SavedStateRegistry中后,SavedStateRegistry进行数据保存时就能调用它的saveState方法,它的svaeState方法前面分析过,会返回需要存储的ViewModel数据

image.png

4、创建和复用SavedStateHandle对象

这里没有复用,每次都是创建一个新的SavedStateHandle对象,但会把旧的数据注入其中

首先,在创建ViewModel时,会通过create方法先创建SavedStateHandleController

image.png

可以看到,在创建前,会拿存储的数据,然后拿存储的数据创建SavedStateHandle,有了SavedStateHandle就能创建SavedStateHandleController对象,然后调用它的attachToLifecycle方法,attachToLifecycle里将SavedStateHandle的savedStateProvider注册到SavedStateRegistry中,之后返回SavedStateHandleController对象

image.png

然后调用ViewModel带有SavedStateHandle参数的构造函数实例化ViewModel

总结:

使用这个方法,SavedStateHandle与SavedStateRegistry直接相连的,数据存储是SavedStateRegistry直接从SavedStateHandle获取,SavedStateHandle需要恢复数据时直接从SavedStateRegistry中获取

三、总结

1、SavedStateRegistry提供了数据存储和恢复的接口,底层利用了Activity老套的数据恢复机制,在Activity的onSaveInstanceState方法中执行存储,在onCreate方法中执行恢复

2、SavedStateRegistry的构造方法是内部方法,我们无法直接实例化,需要使用SavedStateRegistryController,SavedStateRegistryController内部实例化了一个SavedStateRegistry对象,然后提供了与SavedStateRegistry相对的方法。ComponentActivity里就是实例化了一个SavedStateRegistryController对象

3、ViewModel之所以能够很好的防止数据意外丢失,主要有两点,①ViewModelStore的复用,当Activity被意外销毁时,ViewModelStore被保存起来,当Activity被重建时,ViewModel又被取出。②可以使用SavedStateHandle作为ViewModel的参数,手动的存储和恢复一些数据。

4、SavedStateHandle之所以可以恢复数据,得益于它的被注入ViewModel构造方法时的过程。第一种,使用中介SavedStateHandlesProvider,SavedStateHandlesProvider数据通过SavedStateRegistry保存,又为SavedStateHandle提供数据恢复功能,并且SavedStateHandlesProvider还存储SavedStateHandle对象,如果需要的SavedStateHandle已经被存储了,直接拿存储的对象,否则,新建一个SavedStateHandle对象并传入已经保存的数据。第二种通过SavedStateHandleController,因为没有中介,SavedStateHandle的存储和恢复直接依靠SavedStateRegistry,当需要创建ViewModel对象时,会直接新建一个SavedStateHandle对象并传入已经保存的数据。

5、两种创建SavedStateHandle的方法共存可能是为了适配更多的版本,使用SavedStateHandlesProvider的方法可能会更好,因为是后期谷歌加入进去的,在ComponentActivity中实例化ViewModel对象会使用SavedStateHandlesProvider来操纵SavedStateHandle,其他地方如果没有特殊处理会使用另一个方法。