前言
在ViewModel使用以及源码分析(一)中,我们留了一个小尾巴SavedStateHandle没有讲解,这部分设计的内容比较多,本篇开始讲解。
主要就是涉及到保存的一些知识。
1. 概念
1.1 Activity 的重建
我们知道,Activity 被意外销毁的情况可以分为两种:
- 由于屏幕旋转等配置更改的原因导致 Activity 被销毁
- 由于系统资源限制导致 Activity 被销毁
对于以上两种情况,我们当然希望 Activity 重建时之前加载的数据以及用户状态都能够得到恢复,每种情况目前都有着不同的恢复方法
-
对于第一种情况,Jetpack 提供了 ViewModel 这个组件来解决这个问题。ViewModel 可以在屏幕旋转后继续存留,适合用于在内存中存储比较复杂或者量比较大的数据,例如:用 RecyclerView 加载的多个列表项对应的 Data。但当第二种情况发生时 ViewModel 是无法被保留下来的,此后当用户重新回到 Activity 时,只会得到一个全新的 ViewModel 实例并且也需要重新加载列表数据。
-
对于第二种情况,需要依赖于 Activity 原生提供的数据保存及恢复机制,即依赖以下两个方法来实现数据的保存和恢复逻辑。onSaveInstanceState(Bundle) 方法保存的数据在配置更改和 Activity 被意外杀死时都会被保留,但也有着存储容量和存取速度的限制。因为 Bundle 有着容量限制,不适合用于存储大量数据,且 onSaveInstanceState(Bundle) 方法会将数据序列化到磁盘,如果要保存的数据很复杂,序列化会消耗大量的内存和时间。所以 onSaveInstanceState(Bundle) 仅适合用于存储少量的简单类型的数据
- onSaveInstanceState(Bundle)。用于保存数据
- onCreate(Bundle?) 或者 onRestoreInstanceState(Bundle)。用于恢复数据
Google 官方也对这两种情况进行了对比:
对于第二种情况,因为数据的保存和恢复流程被限制在了 Activity 的特定方法里,我们无法直接在 ViewModel 中决定哪些数据需要被保留,也无法直接拿到恢复后的数据,使得整个重建流程和 ViewModel 分裂开了
为了解决这个问题,Jetpack 提供了 SavedStateHandle 这么一个组件,可以看做是对 ViewModel 的功能扩展,使得开发者可以直接在 ViewModel 中直接操作整个数据的重建过程
1.2 SavedState 概念
SavedState 是为了弥补 ViewModel 无法直接感知 onSaveInstanceState 被触发的时机的缺陷而产生的。
在页面即将被销毁的时候,每个使用 SavedState 的 ViewModel 都会创建一个 Bundle 来存储自己的这份数据,最后这些 Bundle 会被汇总到一个 Bundle 中,然后再保存到 onSaveInstanceState(Bundle outState) 的 outState 中;
当页面恢复的时候,会从 onCreate(Bundle savedInstanceState) 中的 savedInstanceState 中取出原来存放的总的那个 Bundle,然后再取出一个个的属于 ViewModel 的子 Bundle,从而实现在 ViewModel 中复用之前存储的数据;
其实就是利用 Bundle 可以保存另一个 Bundle 这么一个特点,分层分区保存数据,让数据之间相互分离,进而方便整存整取;
2. 使用实例
2.1ViewModel 搭配 SavedState 实现数据复用
核心思想
当 Activity 的 onSaveInstanceState 方法被执行的时候,会触发 SavedStateHandle 的 Bundle saveState() 方法,从而保存其数据;当 Activity 被恢复的时候,在新建 ViewModel 实例对象的时候,会从 Activity 的 savedInstanceState 中提取之前存储数据,然后构造 SavedStateHandle 对象并把恢复的数据赋值给该对象,随后把 SavedStateHandle 传递到 ViewModel 中,从而复用其数据;
class SavedStateViewModel extends ViewModel {
// 需要引用 SavedStateHandle
private SavedStateHandle mHandle;
public SavedStateViewModel(SavedStateHandle handle) {
mHandle = handle;
// 实现数据的恢复
Object text = mHandle.get("text");
if (null == text) {
String time = String.valueOf(System.currentTimeMillis() / 1000);
// 实现数据的保存
mHandle.set("text", time);
Log.e("SavedStateViewModel 初始化数据 = " + time);
} else {
Log.e("SavedStateViewModel 恢复数据 = " + text);
}
}
}
// ViewModelActivity.java
class AcaActivity extends AppCompatActivity {
SavedStateViewModel mSavedStateViewModel;
void onCreate(Bundle savedInstanceState) {
// 这边创建时传入了 SavedStateViewModelFactory
mSavedStateViewModel = new ViewModelProvider(this).get(SavedStateViewModel.class);
Log.e("mSavedStateViewModel hashCode = " + mSavedStateViewModel.hashCode());
}
}
在ViewModel使用以及源码分析(一)中,构造了一个UserViewModel类,参数只有application
public UserViewModel(@NonNull Application application) {
}
这里是构造了一个带SavedStateHandle参数的ViewModel类,所以用户可以构造四种ViewModel类。
1. public UserViewModel() {
}
2. public UserViewModel(@NonNull Application application) {
}
3. public UserViewModel(SavedStateHandle handle) {
}
4. public UserViewModel(@NonNull Application application, SavedStateHandle handle) {
}
3. 源码分析
我们先来看一下SavedState components里面几个组件,分别是:
- SavedStateRegistryOwner
- SavedStateRegistryController
- SavedStateRegistry
- SavedStateProvider。
- SavedStateHandle
一下子怼上来5个不明所以的类,我猜测大家多多少少都有点懵B。在这里我先分别看5个类的作用:
-
SavedStateRegistryOwner:一个接口,有一个getSavedStateRegistry 方法,作用是提供SavedStateRegistry对象。该接口主要实现类有Activity和Fragment。
-
SavedStateRegistryController:SavedStateRegistry的控制类,主要有两个方法:performRestore方法的作用恢复数据;performSave方法主要保存数据。Activity和Fragment直接操作类就是该类。
-
SavedStateRegistry: 主要是从UI控制器(Activity或者Fragment等)恢复数据,或者将需要保存的数据写入UI控制器的Bundle里面;外部可以通过registerSavedStateProvider方法注册SavedStateProvider,这样SavedStateRegistry在保存数据会SavedStateProvider提供的数据。SavedStateRegistryController主要操作类就是该类。
-
SavedStateProvider: 主要是提供保存和恢复的数据。该接口只有一个saveState方法,主要的作用将需要保存的数据用Bundle包装起来。
-
SavedStateHandle:SavedStateRegistry 封装了 Activity 层次进行存数据和恢复数据的逻辑,恢复后的数据也需要转交给 SavedStateHandle。
因为 SavedStateHandle 是作为 ViewModel 的构造参数来使用的,我们在 ViewMode 中能直接接触到的都是 SavedStateHandle。
可以用一张图来描述他们之间的关系:
3.1 SavedState 相关源码分析
SavedState 涉及的类结构图及其中的组成成员
SavedStateRegistry 模型图示
一个总 Bundle ,以 key-value 形式存储着每个 ViewModel 对应的子 Bundle;
SavedState 数据存储流程
3.2 ComponentActivity
无论是SavedStateHandle还是其他类,都在Activity里面集中,一切都得从最重要的ComponentActivity讲起。大局观毕竟是最重要的。
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner,
ActivityResultRegistryOwner,
ActivityResultCaller {
}
在ViewModel使用以及源码分析(一)中我们讲解了实现ViewModelStoreOwner和HasDefaultViewModelProviderFactory的代码。这里还要继续讲解这个SavedStateRegistryOwner接口,听名字就知道干什么用的,保存状态的。
public interface SavedStateRegistryOwner extends LifecycleOwner {
@NonNull
SavedStateRegistry getSavedStateRegistry();
}
接口就一个方法,看一下ComponentActivity里面的实现
@Override
public final SavedStateRegistry getSavedStateRegistry() {
return mSavedStateRegistryController.getSavedStateRegistry();
}
实现方法就是调用了mSavedStateRegistryController的getSavedStateRegistry方法,这个mSavedStateRegistryController就是Activity的变量。马上分析~~
分析Activity总是要分析一下onCreate,继续看一下
@Override
private final SavedStateRegistryController mSavedStateRegistryController =
SavedStateRegistryController.create(this);
private ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry() {
...}
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSavedSt