SavedStateHandle组件解析

作者: 愿天堂没Android | 来源:发表于2022-02-25 22:04 被阅读0次

    一、为什么使用SavedStateHandle

    Activity被意外销毁的情况有两种

    1. 资源配置变更导致Activity销毁,例如屏幕旋转
    2. 系统资源限制导致Activity被销毁
    ViewModel`可以解决第一种情况下的数据恢复问题,但是对于第二种,则需要依赖原生提供的数据保护与恢复机制,即`onSaveInstanceState(Bundle)`和`onRestoreInstanceState(Bundle)
    

    onSaveInstanceState(Bundle) 方法保存的数据在资源配置更改或 Activity 被意外杀死时都会被保留,但存在存储容量和存取速度的限制。因为 Bundle 有着容量限制,不适合用于存储大量数据,且 onSaveInstanceState(Bundle) 方法会将数据序列化到磁盘,如果要保存的数据很复杂,序列化会消耗大量的内存和时间。

    所以 onSaveInstanceState(Bundle) 仅适合用于存储少量的简单类型的数据

    对于Activity的第二种销毁情况,数据的保存和恢复被限制在Activity 的特定方法里,无法直接在ViewModel中决定哪些数据需要保留,也无法直接拿到恢复后的数据。

    SavedStateHandle组件可以看作是ViewModel功能的扩展,解决ViewModel无法感知onSaveInstanceState被触发的问题

    二、基本使用

    2.1 简单使用

    SavedStateHandle的使用流程一般为:

    1. SavedStateHandle 作为 ViewModel 的构造参数
    2. ViewModel 内部通过 SavedStateHandle.getLiveData方法来生成一个 LiveData 对象,LiveData 中的数据即我们想要持久化保存的数据。如果是全新启动 ActivityLiveData 中保存的值为 null;如果是重建后的 ActivityLiveData 中保存的值则为重建前其自身的值
    3. 传给getLiveData方法的 String 参数是一个唯一 Key,最终保存到 Bundle 中的键值对就以该值作为 Key,以 LiveData 的值作为 value

    看一下SavedStateHandle实例演示

    SavedStateHandle作为ViewModel的构造参数

    class SavedStateViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
    
        companion object {
            private const val KEY_NAME = "keyName"
        }
    
        //通过 SavedStateHandle.getLiveData方法来生成一个 LiveData 对象
        val nameLiveData = savedStateHandle.getLiveData<String>(KEY_NAME)
        val blogLiveData = MutableLiveData<String>()
    }
    

    ViewModel使用

    class MainActivity : AppCompatActivity() {
    
        private val savedStateViewModel by lazy {
            ViewModelProvider(this).get(SavedStateViewModel::class.java)
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            log("savedStateViewModel: $savedStateViewModel")
            log("savedStateViewModel.name: ${savedStateViewModel.nameLiveData.value}")
            log("savedStateViewModel.blog: ${savedStateViewModel.blogLiveData.value}")
            log("onCreate")
            btn_test.setOnClickListener {
                savedStateViewModel.nameLiveData.value = "SavedStateViewModel"
                savedStateViewModel.blogLiveData.value = "https://https://zhewendev.github.io/"
            }
        }
    
        private fun log(log: String) {
            Log.e("MainActivity", log)
        }
    
    }
    //打印结果
    /**MainActivity第一次启动时**/
    E/MainActivity: savedStateViewModel: com.zhewen.navigationcodelab.navigation.SavedStateViewModel@53e1b17
    E/MainActivity: savedStateViewModel.name: null
    E/MainActivity: savedStateViewModel.blog: null
    E/MainActivity: onCreate
    
    /**按钮点击赋值后,按Home键退出,再重新进入*/
    E/MainActivity: savedStateViewModel: com.zhewen.navigationcodelab.navigation.SavedStateViewModel@b613350
    E/MainActivity: savedStateViewModel.name: SavedStateViewModel
    E/MainActivity: savedStateViewModel.blog: null
    E/MainActivity: onCreate
    

    可通过打开开发者模式中"不保留活动"的选项来模拟 Activity由于系统内存不足被销毁的情况

    从log的打印结果可知,ViewModel已经被销毁重新构建了一个,但是通过SavedStateHandle 构建的 nameLiveData 中还保留着之前的值

    SavedStateHandle 其实也是通过封装 onSaveInstanceState(Bundle)onCreate(Bundle)两个方法来实现的

    SavedStateHandle 会在 Activity 被销毁时通过onSaveInstanceState(Bundle)方法将数据保存在 Bundle 中,在重建时又将数据从 onCreate(Bundle?)中取出,开发者只负责向 SavedStateHandle 存取数据即可,并不需要和 Activity 直接做交互,从而简化了整个开发流程

    2.2 支持类型

    保留在 SavedStateHandle 中的数据将作为BundleActivityFragmentonSaveInstanceState(Bundle)其余部分一起保存和恢复。

    直接支持类型

    默认情况下,您可以对 SavedStateHandle 调用 set()get(),以处理与 Bundle 相同的数据类型

    类型/类支持 数组支持

    如果该类没有扩展上述列表中的任何一项,可考虑通过@Parcelize kotlin注解或直接实现Parcelable来使该类变为Parcelable类型。

    保存非Parcelable

    如果某个类未实现 ParcelableSerializable 且不能修改为实现这些接口之一,则无法直接将该类的实例保存到 SavedStateHandle 中。

    Lifecycle 2.0.0-alpha03开始, SavedStateHandle允许保存任何对象。

    具体方法:使用setSavedStateProvider()方法提供自己的逻辑用于将对象作为Bundle来保存和恢复

    看一下官方提供的示例

    class TempFileViewModel : ViewModel() {
        private var tempFile: File? = null
    
        fun createOrGetTempFile(): File {
            return tempFile ?: File.createTempFile("temp", null).also {
                tempFile = it
            }
        }
    }
    

    为确保临时文件在 Activity 的进程终止随后又恢复后不会丢失,TempFileViewModel 可以使用 SavedStateHandle 保留其数据

    private fun File.saveTempFile() = bundleOf("path", absolutePath)
    
    class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
        private var tempFile: File? = null
        init {
            savedStateHandle.setSavedStateProvider("temp_file") { // saveState()
                if (tempFile != null) {
                    tempFile.saveTempFile()
                } else {
                    Bundle()
                }
            }
        }
    
        fun createOrGetTempFile(): File {
            return tempFile ?: File.createTempFile("temp", null).also {
                tempFile = it
            }
        }
    }
    

    如需在用户返回时恢复 File 数据,请从 SavedStateHandle 中检索 temp_file Bundle。这正是 saveTempFile() 提供的包含绝对路径的 Bundle。该绝对路径随后可用于实例化新的 File

    private fun File.saveTempFile() = bundleOf("path", absolutePath)
    
    private fun Bundle.restoreTempFile() = if (containsKey("path")) {
        File(getString("path"))
    } else {
        null
    }
    
    class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
        private var tempFile: File? = null
        init {
            val tempFileBundle = savedStateHandle.get<Bundle>("temp_file")
            if (tempFileBundle != null) {
                tempFile = tempFileBundle.restoreTempFile()
            }
            savedStateHandle.setSavedStateProvider("temp_file") { // saveState()
                if (tempFile != null) {
                    tempFile.saveTempFile()
                } else {
                    Bundle()
                }
            }
        }
    
        fun createOrGetTempFile(): File {
          return tempFile ?: File.createTempFile("temp", null).also {
              tempFile = it
          }
        }
    }
    

    三、原理解析

    先看一下其核心角色

    • SavedStateRegistryOwner
      一个接口,用于标记其实现类(Activity/Fragment)拥有着数据重建的能力,即作用是提供SavedStateRegistry对象。该接口主要实现类有Activity和Fragment。
    • SavedStateRegistryController
      SavedStateRegistry的控制类,用于创建SavedStatedRegistry,用于连接Activity/Fragment和SavedStateRegistry
    • SavedStateRegistry
      数据存储与恢复中心类
    • SaveStateProvider
      提供保存和恢复的数据。该接口只有一个saveState方法,主要的作用将需要保存的数据用Bundle包装起来。
    • ......

    关于这些核心角色之间的关系,可参看下图:

    img

    SavedStateHandle的工作原理,简单总结为:

    1. 数据的保存是在ActivityonSaveInstanceState()中调用了SavedStateRegistryControllerperformSave()方法来实现
    2. SavedStateRegistryControllerSavedStateRegistry的控制类,关于数据的保存和恢复都转发给了该类处理,performSave()方法最终最后转交到SavedStateRegistryperformSave()中。
    3. performSave()主要是将需要保存的数据写入到ActivityBundle对象实现
    4. 数据的恢复即在onCreate()调用了performRestore()方法,将保存的数据取出恢复
    5. 对于需要保存的数据,实现SavedStateProvider接口,注册一下需要保存的数据;取回数据时;外部通过使用和传给 registerSavedStateProvider() 方法时一样的 key 来取数据,并在取了之后将数据从 mRestoredState 中移除。
    6. ViewModel创建时默认已经实现了SavedStateProvider等接口,实现了数据保存时从ViewModel中获取数据,恢复时给ViewModel赋值。
    7. 存储的数据不能存超过1M

    3.1 数据保存

    SavedStateRegistryOwner用来提供SavedStateRegistry对象,其实现类拥有数据重建能力

    public interface SavedStateRegistryOwner extends LifecycleOwner {
        /**     * Returns owned {@link SavedStateRegistry}     */
        @NonNull
        SavedStateRegistry getSavedStateRegistry();
    }
    

    Activity为例,看一下ComponentActivity

    public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
    LifecycleOwner,
    ViewModelStoreOwner,
    HasDefaultViewModelProviderFactory,
    SavedStateRegistryOwner,
    OnBackPressedDispatcherOwner,
    ActivityResultRegistryOwner,
    ActivityResultCaller {
    
        final SavedStateRegistryController mSavedStateRegistryController =
            SavedStateRegistryController.create(this);
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            mSavedStateRegistryController.performRestore(savedInstanceState);
            ......
        }
    
        @CallSuper
        @Override
        protected void onSaveInstanceState(@NonNull Bundle outState) {
    
            ......
                mSavedStateRegistryController.performSave(outState);
            mActivityResultRegistry.onSaveInstanceState(outState);
        }
    
        @NonNull
        @Override
        public final SavedStateRegistry getSavedStateRegistry() {
            return mSavedStateRegistryController.getSavedStateRegistry();
        }
        ......
    }
    
    1. ComponentActivity实现了SavedStateRegistryOwner接口,通过SavedStateRegistryController来创建SavedStateRegistry对象
    2. onCreate()方法中调用了SavedStateRegistryControllerperformRestore()方法,用于恢复已保存的数据
    3. onDestroy()方法中调用了SavedStateRegistryControllerperformSave()方法,用于保存数据。

    SavedStateRegistryController

    SavedStateRegistryControllerSavedStateRegistry的控制类,数据的保存和恢复都转发给了该类处理。

    看一下其内部实现。

    public final class SavedStateRegistryController {
        private final SavedStateRegistryOwner mOwner;
        private final SavedStateRegistry mRegistry;
    
        private SavedStateRegistryController(SavedStateRegistryOwner owner) {
            mOwner = owner;
            mRegistry = new SavedStateRegistry();
        }
    
        @NonNull
        public SavedStateRegistry getSavedStateRegistry() {
            return mRegistry;
        }
    
        @MainThread
        public void performRestore(@Nullable Bundle savedState) {
            Lifecycle lifecycle = mOwner.getLifecycle();
            //必须在 Activity 的 onCreate 方法调用结束前进行数据恢复
            if (lifecycle.getCurrentState() != Lifecycle.State.INITIALIZED) {
                throw new IllegalStateException("Restarter must be created only during "
                                                + "owner's initialization stage");
            }
            lifecycle.addObserver(new Recreator(mOwner));
            mRegistry.performRestore(lifecycle, savedState);
        }
    
        @MainThread
        public void performSave(@NonNull Bundle outBundle) {
            mRegistry.performSave(outBundle);
        }
    
        @NonNull
        public static SavedStateRegistryController create(@NonNull SavedStateRegistryOwner owner) {
            return new SavedStateRegistryController(owner);
        }
    }
    

    SavedStateRegistryController最终将数据的保存和恢复转交给SavedStateRegistry的同名方法实现。

    数据的保存是在ActivityonSaveInstanceState()中调用了SavedStateRegistryControllerperformSave()方法,最后转交到SavedStateRegistryperformSave()中。

    SavedStateRegistry

    看一下SavedStateRegistry类的performSave()实现

    public final class SavedStateRegistry {
        private static final String SAVED_COMPONENTS_KEY =
            "androidx.lifecycle.BundlableSavedStateRegistry.key";
        private SafeIterableMap<String, SavedStateProvider> mComponents =
            new SafeIterableMap<>();
        @Nullable
        private Bundle mRestoredState;
        private boolean mRestored;
    
        @MainThread
        void performSave(@NonNull Bundle outBundle) {
            Bundle components = new Bundle();
            //mRestoredState不为空,表示之前恢复的数据还没有被消费完,需要将没有消费的数据再一次保存。
            if (mRestoredState != null) {
                components.putAll(mRestoredState);
            }
            //遍历所有注册的SavedStateProvider,将所有的SavedStateProvider提供的数据保存起来。即把所有需要保存的Bundle通过outBundle保存起来
            for (Iterator<Map.Entry<String, SavedStateProvider>> it =
                 mComponents.iteratorWithAdditions(); it.hasNext(); ) {
                Map.Entry<String, SavedStateProvider> entry1 = it.next();
                components.putBundle(entry1.getKey(), entry1.getValue().saveState());
            }
            //将所有保存的数据全部写入到Activity的Bundle对象
            outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
        }
    
        ......
    
        public interface SavedStateProvider {
            @NonNull
            Bundle saveState();//将需要保存的数据用Bundle包装起
        }
    }
    

    performSave()的主要内容:

    1. 判断是否存在上一次恢复的数据,如果存在将其添加保存;
    2. 将所有需要保存的Bundle保存起来;
    3. 将所有保存的数据写入到ActivityBundle对象

    小结

    1. 数据的保存是在ActivityonSaveInstanceState()中调用了SavedStateRegistryControllerperformSave()方法来实现
    2. SavedStateRegistryControllerSavedStateRegistry的控制类,关于数据的保存和恢复都转发给了该类处理,performSave()方法最终最后转交到SavedStateRegistryperformSave()中。
    3. performSave()主要是将需要保存的数据写入到ActivityBundle对象实现

    3.2 数据恢复

    关于数据恢复,直接看一下SavedStateRegistryperformRestore()

    @MainThread
    void performRestore(@NonNull Lifecycle lifecycle, @Nullable Bundle savedState) {
        //已恢复过,只能恢复一次
        if (mRestored) {
            throw new IllegalStateException("SavedStateRegistry was already restored.");
        }
        //待恢复数据不为空,将其取出
        if (savedState != null) {
            mRestoredState = savedState.getBundle(SAVED_COMPONENTS_KEY);
        }
    
        //通过监听 Lifecycle 来确定当前是否处于可以恢复数据的生命周期阶段,用一个布尔变量 mAllowingSavingState 来标记
        lifecycle.addObserver(new GenericLifecycleObserver() {
            @Override
            public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_START) {
                    mAllowingSavingState = true;
                } else if (event == Lifecycle.Event.ON_STOP) {
                    mAllowingSavingState = false;
                }
            }
        });
    
        //标记已恢复
        mRestored = true;
    }
    

    小结

    数据恢复即在onCreate()调用了performRestore()方法,将保存的数据取出恢复

    3.3 数据入口与消费

    数据保存时是如何从ViewModel中获取数据的?数据恢复时又是如何给ViewModel赋值的?

    数据保存入口

    数据保存时,有需要保存的数据需要添加到SafeIterableMap<String, SavedStateProvider> mComponents中,SavedStateRegistry提供了一个入口方法registerSavedStateProvider()

    外部需要实现 SavedStateProvider 接口,在 saveState()返回想要保存的数据,然后调用registerSavedStateProvider 方法将 SavedStateProvider 对象提交给 SavedStateRegistry

    @MainThread
    public void registerSavedStateProvider(@NonNull String key,                                       @NonNull SavedStateProvider provider) {
        //注册想要保存的数据,等到 performSave再保存。外部通过一个唯一标识 key 来和要保存的数据 Bundle 相对应,
        SavedStateProvider previous = mComponents.putIfAbsent(key, provider);
        if (previous != null) {
            throw new IllegalArgumentException("SavedStateProvider with the given key is"
                                               + " already registered");
        }
    }
    

    想要通过SavedStateRegistry保存数据,只需要实现SavedStateProvider接口,注册一下就可以了;取回数据时,通过原来的key取回即可。

    数据消费入口

    数据消费,即需要开发者通过取键值对的方式来消费数据,将用户数据或者 UI 状态恢复到销毁前的状态。

    消费数据的入口就是 consumeRestoredStateForKey()方法

    @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;
    }
    

    外部通过使用和传给 registerSavedStateProvider() 方法时一样的 key 来取数据,并在数据获取后将数据从 mRestoredState 中移除。

    如果所有数据都被消费了的话,那么就将 mRestoredState 置为 null,标记着所有数据都已经被消费完了

    3.4 入口函数调用

    看一下何处调用了registerSavedStateProvider()consumeRestoredStateForKey()

    ActivityFragment默认实现了HasDefaultViewModelProviderFactory接口,默认返回SavedStateViewModelFactory,看一下其内部如何创建ViewModel实例。

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        //判断viewmodel 是否为 AndroidViewModel的子类
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        if (isAndroidViewModel && mApplication != null) {
            //是AndroidViewModel的子类,尝试查找它是否拥有(Application,SavedStateHandle)两个参数的构造函数
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            //查找它是否拥有(SavedStateHandle)一个参数的构造函数
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // doesn't need SavedStateHandle
        if (constructor == null) {
            //上面两种方式都没找到,使用AndroidViewModelFactory创建viewmodel实例。可以无参数,也可以携带一个(Application)参数
            return mFactory.create(modelClass);
        }
    
        //构建出SavedStateHandleController对象
        SavedStateHandleController controller = SavedStateHandleController.create(
            mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
        try {
            T viewmodel;
            if (isAndroidViewModel && mApplication != null) {
                //反射构造ViewModel实例对象的时候,把参数一同传递了进去。
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
                viewmodel = constructor.newInstance(controller.getHandle());
            }
            viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
            //返回新创建的ViewModel实例对象
            return viewmodel;
        }
        ......
    }
    

    SavedStateViewModelFactorycreate()方法主要内容为

    1. 判断需要创建的ViewModel是否是AndroidViewModel子类
    2. 判断ViewModel是否拥有参数SavedStateHandle的构造器。如果没有,使用AndroidViewModelFactory创建viewmodel实例;如果有,构建SavedStateHandleController对象
    3. 反射构造ViewModel实例对象。

    SavedStateHandleController

    看一下SavedStateHandleControllercreate()方法

    static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,                                         String key, Bundle defaultArgs) {
        //从数据中心SavedStateRegistry获取该ViewModel的Bundle数据对象  
        Bundle restoredState = registry.consumeRestoredStateForKey(key);
        //构建SavedStateHandle对象,并把restoredState中的数据再拆分出来,存储到mRegular这个 map集合
        SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
        //构建SavedStateHandleController对象
        SavedStateHandleController controller = new SavedStateHandleController(key, handle);
        //把SavedStateHandle的需要保存的数据注册到了SavedStateRegistry中
        controller.attachToLifecycle(registry, lifecycle);
        tryToAddRecreator(registry, lifecycle);
        return controller;
    }
    void attachToLifecycle(SavedStateRegistry registry, Lifecycle lifecycle) {
        if (mIsAttached) {
            throw new IllegalStateException("Already attached to lifecycleOwner");
        }
        mIsAttached = true;
        lifecycle.addObserver(this);
        registry.registerSavedStateProvider(mKey, mHandle.savedStateProvider());
    }
    

    SavedStateHandleControllercreate()方法的主要内容

    1. 获取数据缓存;
    2. 构建SavedStateHandle对象,对数据缓存进行拆分,存储;
    3. 构建SavedStateHandleController对象,把SavedStateHandle的需要保存的数据注册到了SavedStateRegistry中。

    其中:

    1. SavedStateHandleController中的key就是ViewModelkey
    2. defaultArgs对于Activity而言就是getIntent().getExtras(),对于Fragment而言就是getArguments()

    3.5 getLiveData

    前述示例通过 SavedStateHandle.getLiveData方法来生成一个 LiveData 对象来存放持久化存放的数据。

    看一下SavedStateHandle

    public final class SavedStateHandle {
        final Map<String, Object> mRegular;
        final Map<String, SavedStateProvider> mSavedStateProviders = new HashMap<>();
        private final Map<String, SavingStateLiveData<?>> mLiveDatas = new HashMap<>();
    
        public SavedStateHandle(@NonNull Map<String, Object> initialState) {
            mRegular = new HashMap<>(initialState);
        }
    
        public SavedStateHandle() {
            mRegular = new HashMap<>();
        }
    
        @MainThread
        public boolean contains(@NonNull String key) {
            return mRegular.containsKey(key);
        }
    
        @MainThread
        @NonNull
        public <T> MutableLiveData<T> getLiveData(@NonNull String key) {
            return getLiveDataInternal(key, false, null);
        }
    
        @MainThread
        @Nullable
        public <T> T get(@NonNull String key) {
            return (T) mRegular.get(key);
        }
        ......
    }
    

    SavedStateHandle 包含两个构造函数

    1. initialState 中保存的即是 Activity 重建时保留下来的的键值对数据
    2. mRegular 中保存的即是最终要持久化保存的键值对数据

    如果调用的有参构造函数,则代表着此次初始化是 Activity 被销毁重建的情况;如果调用的是无参构造函数,则代表着此次初始化是 Activity 全新启动的情况

    看一下SavedStateHandle中的getLiveData方法

    @MainThread
    @NonNull
    public <T> MutableLiveData<T> getLiveData(@NonNull String key) {
        return getLiveDataInternal(key, false, null);
    }
    
    @MainThread
    @NonNull
    public <T> MutableLiveData<T> getLiveData(@NonNull String key,                                          @SuppressLint("UnknownNullness") T initialValue) {
        return getLiveDataInternal(key, true, initialValue);
    }
    @NonNull
    private <T> MutableLiveData<T> getLiveDataInternal(    @NonNull String key,    boolean hasInitialValue,    @Nullable T initialValue) {
        MutableLiveData<T> liveData = (MutableLiveData<T>) mLiveDatas.get(key);
        if (liveData != null) {
            return liveData;
        }
        SavingStateLiveData<T> mutableLd;
        // double hashing but null is valid value
        if (mRegular.containsKey(key)) {
            mutableLd = new SavingStateLiveData<>(this, key, (T) mRegular.get(key));
        } else if (hasInitialValue) {
            mutableLd = new SavingStateLiveData<>(this, key, initialValue);
        } else {
            mutableLd = new SavingStateLiveData<>(this, key);
        }
        mLiveDatas.put(key, mutableLd);
        return mutableLd;
    }
    

    getLiveData()方法的主要内容

    返回一个和 key 还有 mRegular关联的 LiveData 对象。LiveData 对象的初始默认值会从mRegularinitialValue两个之间选取,每次生成的 LiveData 对象也都会被保存在 mLiveDatas 中,以便后续复用

    SavingStateLiveData

    当外部对 LiveData 进行值更新操作时,SavedStateHandle 需要拿到最新值,因为最终持久化保存的肯定也需要是最新值。

    getLiveDataInternal方法返回的 SavingStateLiveData 对象会在 setValue 方法被调用后,同步更新 mRegular 中的键值对数据。

    static class SavingStateLiveData<T> extends MutableLiveData<T> {
        private String mKey;
        private SavedStateHandle mHandle;
    
        @Override
        public void setValue(T value) {
            if (mHandle != null) {
                //同步更新mRegular中键值对
                mHandle.mRegular.put(mKey, value);
            }
            super.setValue(value);
        }
    
        void detach() {
            mHandle = null;
        }
        ......
    }
    

    setSavedStateProvider 方法

    SavedStateHandle 开放了一个 setSavedStateProvider 方法交由外部来传入 SavedStateProvider 对象,外部负责实现 saveState()方法来返回想要持久化缓存的 Bundle 对象,由 SavedStateHandle 来负责调用该方法

    @MainThread
    public void setSavedStateProvider(@NonNull String key, @NonNull SavedStateProvider provider) {
        mSavedStateProviders.put(key, provider);
    }
    private final SavedStateProvider mSavedStateProvider = new SavedStateProvider() {
        @SuppressWarnings("unchecked")
        @NonNull
        @Override
        public Bundle saveState() {
            // Get the saved state from each SavedStateProvider registered with this
            // SavedStateHandle, iterating through a copy to avoid re-entrance
            Map<String, SavedStateProvider> map = new HashMap<>(mSavedStateProviders);
            for (Map.Entry<String, SavedStateProvider> entry : map.entrySet()) {
                Bundle savedState = entry.getValue().saveState();
                set(entry.getKey(), savedState);
            }
            // Convert the Map of current values into a Bundle
            Set<String> keySet = mRegular.keySet();
            ArrayList keys = new ArrayList(keySet.size());
            ArrayList value = new ArrayList(keys.size());
            for (String key : keySet) {
                keys.add(key);
                value.add(mRegular.get(key));
            }
    
            Bundle res = new Bundle();
            // "parcelable" arraylists - lol
            res.putParcelableArrayList("keys", keys);
            res.putParcelableArrayList("values", value);
            return res;
        }
    };
    

    SavedStateHandle原理总结

    1. 数据的保存是在ActivityonSaveInstanceState()中调用了SavedStateRegistryControllerperformSave()方法来实现
    2. SavedStateRegistryControllerSavedStateRegistry的控制类,关于数据的保存和恢复都转发给了该类处理,performSave()方法最终最后转交到SavedStateRegistryperformSave()中。
    3. performSave()主要是将需要保存的数据写入到ActivityBundle对象实现
    4. 数据的恢复即在onCreate()调用了performRestore()方法,将保存的数据取出恢复
    5. 对于需要保存的数据,实现SavedStateProvider接口,注册一下需要保存的数据;取回数据时;外部通过使用和传给 registerSavedStateProvider() 方法时一样的 key 来取数据,并在取了之后将数据从 mRestoredState 中移除。
    6. ViewModel创建时默认已经实现了SavedStateProvider等接口,实现了数据保存时从ViewModel中获取数据,恢复时给ViewModel赋值。
    7. 存储的数据不能存超过1M

    作者:者文
    链接:https://juejin.cn/post/7067899196220637220
    如有侵权,请联系删除!

    相关文章

      网友评论

        本文标题:SavedStateHandle组件解析

        本文链接:https://www.haomeiwen.com/subject/xsyfrrtx.html