SavedState数据恢复原理

  • 概述

    onSaveInstanceState() 回调会存储一些数据,如果系统销毁后又重新创建界面控制器(如 Activity 或 Fragment),则需要使用这些数据重新加载该控制器的状态。如需了解如何实现已保存实例状态,请参阅 Activity 生命周期指南中的“保存和恢复 Activity 状态”。

    已保存实例状态捆绑包在配置更改和进程终止后都会保留,但受限于存储容量和速度,因为 onSavedInstanceState() 会将数据序列化到磁盘。如果序列化的对象很复杂,序列化会占用大量的内存。因为此过程在配置更改期间发生在主线程上,所以长时间运行的序列化可能会导致丢帧和视觉卡顿。

    请勿将 onSavedInstanceState() 用于存储大量的数据(如位图),也不要用于存储需要冗长的序列化或反序列化操作的复杂数据结构,而是只能用于存储基元类型和简单的小对象,例如 String。因此,请使用 onSaveInstanceState() 存储最少量的数据(例如 ID),如果其他持久性机制失效,需要使用这些数据来重新创建必要的数据以将界面恢复到之前的状态。大多数应用都应实现 onSaveInstanceState() 来处理系统发起的进程终止。

    系统会自动恢复界面UI布局状态,但是前提是为了使 Android 系统恢复 Activity 中视图的状态,每个视图必须具有 android:id 属性提供的唯一 ID。

    之前整理过在创建ViewModel的时候构造方法的第二个参数可以传入一个SavedStateHandle对象,前面说过这个对象会包含getIntent的里面拿到的bundle数据。其实在由系统杀死的数据保存和恢复中,需要恢复的数据也会保存到这个对象里面传入ViewModel中,来看看是怎么实现的。

    Android系统原因销毁Activity的时候会杀死该Activity依附的整个进程,在因为内存或者系统原因导致的非用户意愿销毁的Activity都会调用onSaveInstanceState方法保存数据,可以在这个方法里保存需要在重新创建时需要恢复的数据,在onCreate或者onRestoreInstanceState方法里恢复之前保存的数据。

  • 数据保存

    @CallSuper
    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        Lifecycle lifecycle = getLifecycle();
        if (lifecycle instanceof LifecycleRegistry) {
            ((LifecycleRegistry) lifecycle).setCurrentState(Lifecycle.State.CREATED);
        }
        super.onSaveInstanceState(outState);
        mSavedStateRegistryController.performSave(outState);
    }
    

    然后调用SacedStateRegistryController的performSave:

    @MainThread
    public void performSave(@NonNull Bundle outBundle) {
        mRegistry.performSave(outBundle);
    }
    

    然后调用SavedStateRegistry的performSave方法:

    @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());
        }
        outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
    }
    

    mRestoredState是之前恢复的且未取出使用的数据,这里会重新保存。mComponents里面是什么呢?

    我们发现mComponents是在下面这个方法里put的数据:

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

    可以看到,这里面保存的value是SavedStateProvider实例。通过追溯,找到:

    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());
    }
    

    mKey和mHandle是构造方法中传入的:

    static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
            String key, Bundle defaultArgs) {
        Bundle restoredState = registry.consumeRestoredStateForKey(key);
        SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
        SavedStateHandleController controller = new SavedStateHandleController(key, handle);
        controller.attachToLifecycle(registry, lifecycle);
        tryToAddRecreator(registry, lifecycle);
        return controller;
    }
    

    最终会找到创建ViewModel的方法:

    @Override
    public  T create(@NonNull String key, @NonNull Class modelClass) {
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor constructor;
        if (isAndroidViewModel) {
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // doesn't need SavedStateHandle
        if (constructor == null) {
            return mFactory.create(modelClass);
        }
    
        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 (IllegalAccessException e) {
            throw new RuntimeException("Failed to access " + modelClass, e);
        } catch (InstantiationException e) {
            throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("An exception happened in constructor of "
                    + modelClass, e.getCause());
        }
    }
    
    @NonNull
    @Override
    public  T create(@NonNull Class modelClass) {
        // ViewModelProvider calls correct create that support same modelClass with different keys
        // If a developer manually calls this method, there is no "key" in picture, so factory
        // simply uses classname internally as as key.
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return create(canonicalName, modelClass);
    }
    

    可见,key就是ViewModel的类名。再看createHandle:

    static SavedStateHandle createHandle(@Nullable Bundle restoredState,
            @Nullable Bundle defaultState) {
        if (restoredState == null && defaultState == null) {
            return new SavedStateHandle();
        }
    
        Map state = new HashMap<>();
        if (defaultState != null) {
            for (String key : defaultState.keySet()) {
                state.put(key, defaultState.get(key));
            }
        }
    
        if (restoredState == null) {
            return new SavedStateHandle(state);
        }
    
        ArrayList keys = restoredState.getParcelableArrayList(KEYS);
        ArrayList values = restoredState.getParcelableArrayList(VALUES);
        if (keys == null || values == null || keys.size() != values.size()) {
            throw new IllegalStateException("Invalid bundle passed as restored state");
        }
        for (int i = 0; i < keys.size(); i++) {
            state.put((String) keys.get(i), values.get(i));
        }
        return new SavedStateHandle(state);
    }
    

    可以看到,这里会把defaultState(也就是之前文章里说过的getIntent里面携带的数据)和系统原因杀死进程时在onSaveInstanceState方法里保存的且未取出的数据一并保存在新的state里面,最终传给ViewModel中的SavedStateHandle,所以可以在ViewModel中初始化这些数据。

    书归正传,现在找到了mHandle,它的savedStateProvider方法返回的是mSavedStateProvider:

    private final SavedStateProvider mSavedStateProvider = new SavedStateProvider() {
        @SuppressWarnings("unchecked")
        @NonNull
        @Override
        public Bundle saveState() {
            Set 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;
        }
    };
    

    好了我们现在知道了mComponents里面保存了一对键值对,key是对应ViewModel的类名,value是上面SavedStateHandle类里的mSavedStateProvider。

    那么回到开始的SavedStateRegistry的performSave方法,mComponents里面的entry就是SavedStateProvider,所以就是调用它的saveState方法,可以看到,返回了一个Bundle,里面是mRegular里面的数据,mRegular是什么,发现它的值都是在调用它的set方法时put的,set方法在哪调用呢,我们知道,如果在我们继承自AndroidViewModel的自定义ViewModel类中定义了两个参数的构造方法的话(当然第一个参数是application),或者是继承自ViewModel的类中有单一参数是SavedStateHandle的构造方法,那么构造时会自动传入SavedStateHandle,当使用它的set方法去设置数据的时候,set的数据会被保存到mRegular中:

    public  void set(@NonNull String key, @Nullable T value) {
        validateValue(value);
        @SuppressWarnings("unchecked")
        MutableLiveData mutableLiveData = (MutableLiveData) mLiveDatas.get(key);
        if (mutableLiveData != null) {
            // it will set value;
            mutableLiveData.setValue(value);
        } else {
            mRegular.put(key, value);
        }
    }
    

    这里看到非LiveData才会保存到mRegular中,但其实mutableiveata的setValue方法最终也会把数据保存到mRegular中。

  • 数据恢复

    现在我们来看Activity重新创建实例的时候意外销毁的数据如何恢复。

    系统杀死进程时会把onSaveInstanceState中保存的Bundle保存到磁盘中,下次重新创建的时候会重新读取,读取出的数据会传入onCreate方法和onRestoreInstanceState方法中,onRestoreInstanceState方法会在onStart之后调用,这个方法在Activity的默认实现里是用来重新加载布局的,所以我们通常在onCreate里面进行用户数据的恢复。

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSavedStateRegistryController.performRestore(savedInstanceState);
        ReportFragment.injectIfNeededIn(this);
        if (mContentLayoutId != 0) {
            setContentView(mContentLayoutId);
        }
    }
    

    然后调用SavedStateRegistryController的performRestore方法:

    @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");
        }
        lifecycle.addObserver(new Recreator(mOwner));
        mRegistry.performRestore(lifecycle, savedState);
    }
    

    再调用SavedStateRegistry的performRestore方法:

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

    可以看到,这里把之前保存的数据重新读取出来放在mRestoredState中。

    然后在创建ViewModel时,会调用SavedStateHandleController的create方法:

    static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
            String key, Bundle defaultArgs) {
        Bundle restoredState = registry.consumeRestoredStateForKey(key);
        SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
        SavedStateHandleController controller = new SavedStateHandleController(key, handle);
        controller.attachToLifecycle(registry, lifecycle);
        tryToAddRecreator(registry, lifecycle);
        return controller;
    }
    

    看一下consumeRestoredStateForKey方法:

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

    可见,这里会从mRestoredState中读取之前保存的数据,看一下SavedStateHandle.createHandle方法:

    static SavedStateHandle createHandle(@Nullable Bundle restoredState,
            @Nullable Bundle defaultState) {
        if (restoredState == null && defaultState == null) {
            return new SavedStateHandle();
        }
    
        Map state = new HashMap<>();
        if (defaultState != null) {
            for (String key : defaultState.keySet()) {
                state.put(key, defaultState.get(key));
            }
        }
    
        if (restoredState == null) {
            return new SavedStateHandle(state);
        }
    
        ArrayList keys = restoredState.getParcelableArrayList(KEYS);
        ArrayList values = restoredState.getParcelableArrayList(VALUES);
        if (keys == null || values == null || keys.size() != values.size()) {
            throw new IllegalStateException("Invalid bundle passed as restored state");
        }
        for (int i = 0; i < keys.size(); i++) {
            state.put((String) keys.get(i), values.get(i));
        }
        return new SavedStateHandle(state);
    }
    

    这里把之前通过SavedStateHandle的set方法设置的普通类型的数据读取出来,即SavedStateHandle.createHandle方法读取的SavedStateHandle包含了getIntent中的数据、上次数据恢复保存在mRestoredState中且未被读取的数据、还有通过SavedStateHandle设置的数据,然后SavedStateHandle会通过controller.getHandle()传入ViewModel中。

  • 其他

    在SavedStateHandleController.create最后有一句tryToAddRecreator:

    private static void tryToAddRecreator(SavedStateRegistry registry, Lifecycle lifecycle) {
        Lifecycle.State currentState = lifecycle.getCurrentState();
        if (currentState == INITIALIZED || currentState.isAtLeast(STARTED)) {
            registry.runOnNextRecreation(OnRecreation.class);
        } else {
            lifecycle.addObserver(new LifecycleEventObserver() {
                @Override
                public void onStateChanged(@NonNull LifecycleOwner source,
                        @NonNull Lifecycle.Event event) {
                    if (event == Lifecycle.Event.ON_START) {
                        lifecycle.removeObserver(this);
                        registry.runOnNextRecreation(OnRecreation.class);
                    }
                }
            });
        }
    }
    

    这段代码意思就是在INNITIALIZED或STARTED状态之后,调用SavedStateRegistry的runOnNextRecreation方法,且只会调用一次:

    public void runOnNextRecreation(@NonNull Class clazz) {
        if (!mAllowingSavingState) {
            throw new IllegalStateException(
                    "Can not perform this action after onSaveInstanceState");
        }
        if (mRecreatorProvider == null) {
            mRecreatorProvider = new Recreator.SavedStateProvider(this);
        }
        try {
            clazz.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Class" + clazz.getSimpleName() + " must have "
                    + "default constructor in order to be automatically recreated", e);
        }
        mRecreatorProvider.add(clazz.getName());
    }
    

    看似这段代码好像没起什么作用,但在Recreator.SavedStateProvider的构造方法里:

    SavedStateProvider(final SavedStateRegistry registry) {
        registry.registerSavedStateProvider(COMPONENT_KEY, this);
    }
    

    可见向SavedStateRegistry中注册了当前的Recreator.SavedStateProvider,这个类继承自SavedStateRegistry.SavedStateProvider,这个类如下:

    static final class SavedStateProvider implements SavedStateRegistry.SavedStateProvider {
        @SuppressWarnings("WeakerAccess") // synthetic access
        final Set mClasses = new HashSet<>();
    
        SavedStateProvider(final SavedStateRegistry registry) {
            registry.registerSavedStateProvider(COMPONENT_KEY, this);
        }
    
        @NonNull
        @Override
        public Bundle saveState() {
            Bundle bundle = new Bundle();
            bundle.putStringArrayList(CLASSES_KEY, new ArrayList<>(mClasses));
            return bundle;
        }
    
        void add(String className) {
            mClasses.add(className);
        }
    }
    

    mClassed保存了OnRecreation.class,前面我们知道,在后面意外销毁调用performSave时就会调用mComponents中每个SavedStateRegistry.SavedStateProvider的saveState方法,所以在performSave时这里的saveState也会被调用,就会把OnRecreation.class的类名保存进去,那么保存的这个类有什么用呢?

    还记得在performRestore中有一句lifecycle.addObserver(new Recreator(mOwner))吗,Recreator继承自GenericLifecycleObserver,所以Recreator和组件生命周期绑定在一起,在组件相关生命周期方法回调的时候Recreator就会发挥作用:

    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        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);
        if (bundle == null) {
            return;
        }
        ArrayList classes = bundle.getStringArrayList(CLASSES_KEY);
        if (classes == null) {
            throw new IllegalStateException("Bundle with restored state for the component \""
                    + COMPONENT_KEY + "\" must contain list of strings by the key \""
                    + CLASSES_KEY + "\"");
        }
        for (String className : classes) {
            reflectiveNew(className);
        }
    }
    

    这里拿到的bundle就是上面的Receator.SavedStateProvider的saveState方法返回的bundle,然后通过它取得保存的mClasses类名集合,逐个调用reflectiveView方法:

    private void reflectiveNew(String className) {
        Class clazz;
        try {
            clazz = Class.forName(className, false,
                    Recreator.class.getClassLoader()).asSubclass(AutoRecreated.class);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Class " + className + " wasn't found", e);
        }
    
        Constructor constructor;
        try {
            constructor = clazz.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException("Class" + clazz.getSimpleName() + " must have "
                    + "default constructor in order to be automatically recreated", e);
        }
        constructor.setAccessible(true);
    
        AutoRecreated newInstance;
        try {
            newInstance = constructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Failed to instantiate " + className, e);
        }
        newInstance.onRecreated(mOwner);
    }
    

    所以这里会调用OnRecreation的onRecreated方法:

    @Override
    public void onRecreated(@NonNull SavedStateRegistryOwner owner) {
        if (!(owner instanceof ViewModelStoreOwner)) {
            throw new IllegalStateException(
                    "Internal error: OnRecreation should be registered only on components"
                            + "that implement ViewModelStoreOwner");
        }
        ViewModelStore viewModelStore = ((ViewModelStoreOwner) owner).getViewModelStore();
        SavedStateRegistry savedStateRegistry = owner.getSavedStateRegistry();
        for (String key : viewModelStore.keys()) {
            ViewModel viewModel = viewModelStore.get(key);
            attachHandleIfNeeded(viewModel, savedStateRegistry, owner.getLifecycle());
        }
        if (!viewModelStore.keys().isEmpty()) {
            savedStateRegistry.runOnNextRecreation(OnRecreation.class);
        }
    }
    

    可见这里其实就是把其他之前保存的未创建的ViewModel创建并关联到组件生命周期中。

  • 布局保存和恢复

    开头我们提到,布局相关的保存恢复工作系统会帮我们处理,但是必须是声明了id的布局组件。我们可以从代码中证明这一点。

    在Activity的onSaveInstanceState方法中,

    protected void onSaveInstanceState(@NonNull Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    
        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mAutoFillResetNeeded) {
            outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
            getAutofillManager().onSaveInstanceState(outState);
        }
        dispatchActivitySaveInstanceState(outState);
    }
    

    第一行中保存了mWindow.saveHierarchyState(),我们知道mWindow就是PhoneWindow,它的saveHierarchyState方法中又调用了mContentParent.saveHierarchyState(states),就是View中的saveHierarchyState方法,最终调用到View的dispatchSaveInstanceState方法:

    protected void dispatchSaveInstanceState(SparseArray container) {
        if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
            Parcelable state = onSaveInstanceState();
            if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                throw new IllegalStateException(
                        "Derived class did not call super.onSaveInstanceState()");
            }
            if (state != null) {
                // Log.i("View", "Freezing #" + Integer.toHexString(mID)
                // + ": " + state);
                container.put(mID, state);
            }
        }
    }
    

    可以看到,if语句中指定了必须是有id的且mViewFlags不能是SAVE_DISABLED_MASK的情况下才会保存。

    SAVE_DISABLED_MASK是通过布局属性中的saveEnabled属性或者setSaveEnabled方法修改的,默认是保存。

    case com.android.internal.R.styleable.View_saveEnabled:
        if (!a.getBoolean(attr, true)) {
            viewFlagValues |= SAVE_DISABLED;
            viewFlagMasks |= SAVE_DISABLED_MASK;
        }
        break;
    
  • 自定义数据保存和恢复

    Activity和Fragment都可以调用getSavedStateRegistry()方法获取SavedStateRegistry,然后可以调用它的registerSavedStateProvider方法注册自己的SavedStateProvider,用于系统意外杀死进程时保存数据:

    savedStateRegistry.registerSavedStateProvider(MY_BUNDLE_KEY, object : SavedStateRegistry.SavedStateProvider {
        override fun saveState() : Bundle {
            val bundle = Bundle()
            bundle.putString("name", "Lucas")
            bundle.putInt("age", 24)
            return bundle
        }
    })
    

    然后在初始化的时候可以进行数据恢复:

    val restoredData = savedStateRegistry.consumeRestoredStateForKey(MY_BUNDLE_KEY)
    if(restoredData!=null){
        ...
    }
    

你可能感兴趣的:(SavedState数据恢复原理)