[Android] Activity状态保存与恢复

Activity状态保存与恢复


之前没太明白视图在什么时候可以保存状态,在什么时候状态可以被恢复,想了想还是得琢磨琢磨源码,虽然干的活离Android越来越远,但毕竟老本行,还是需要花精力琢磨的。


在不同的版本中,Activity保存状态的时机是不一样的,在Android 3之前,把Activity状态的保存是在onPause之前被调用onSaveInstanceState(ActivityThread#performPauseActivity),而在Android 9之前,把Activity状态的保存后置到了onStop之前,在Android 9之后,把Activity的状态保存后置到了onStop之后(ActivityThread#callActivityOnStop)。

而通过前面的分析:

Android Activity启动流程

Android UI工作流程1

Android UI工作流程2

我们可以知道在AMS创建Activity的时候,通过Binder事务,调用到客户端进程,执行LaunchActivityItem#execute,其中就包括ActivityClientRecord的创建。

 public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
        ActivityClientRecord r = new ActivityClientRecord(..., mState,...);
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    } 

而一个ActivityClientRecord代表着用户进程的一个Activity实例记录。其中的Bundle类型的mState字段就代表着这个Activity的状态。

回看到ActivityThread#callActivityOnStop,最终的逻辑都是调用到ActivityThread#callActivityOnSaveInstanceState方法:

 private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
        // 给ActivityClientRecord#state创建一个新的Bundle对象。
        r.state = new Bundle();
        r.state.setAllowFds(false);// 序列化底层对于是否支持文件描述符的逻辑是两套,这里设置为false,因为这个bundle是不需要保存fd的
        if (r.isPersistable()) { // 如果是系统内置的应用
            r.persistentState = new PersistableBundle();
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                    r.persistentState);
        } else {
            // 对于普通App而言,通过下面这里调用保存状态
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    } 

-> Instrumentation#callActivityOnSaveInstanceState

-> activity.performSaveInstanceState

 final void performSaveInstanceState(@NonNull Bundle outState) {
        dispatchActivityPreSaveInstanceState(outState);// 可以注册ActivityLifecycleCallbacks给Application获取这个hook回调。
        onSaveInstanceState(outState);// 执行Activity实例的onSaveInstanceState方法。
        saveManagedDialogs(outState);// 保存关联了当前Activity的Dialog状态。
        mActivityTransitionState.saveState(outState);// 保存Activity转场动画相关的状态
        storeHasCurrentPermissionRequest(outState);// 保存当前Activity是否正在申请权限
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
        dispatchActivityPostSaveInstanceState(outState);// 和上面的pre类似,也是分发一个hook回调。
    } 

那么核心就是Activity的onSaveInstanceState,我们知道,我们可以通过重写这个方法来实现一些额外的状态保存的逻辑,当然即便我们不重写Activity实例的onSaveInstanceState方法,也有一些Activity的默认逻辑,默认的实现是:

 protected void onSaveInstanceState(@NonNull Bundle outState) {
        // 核心通过Window的saveHierarchyState保存视图状态。
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        // 内部通过FragmentManager保存Activity持有的所有Fragment状态
        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);
    } 

View的状态保存和恢复:

View的状态保存:

而Window实例自然就是PhoneWindow:

 public Bundle saveHierarchyState() {
        Bundle outState = new Bundle();
        // 如果Window没有内容,就无需保存什么
        // 而mContentParent其实就是Activity结构中的R.id.content的FrameLayout
        if (mContentParent == null) {
            return outState;
        }
        // 创建一个稀松数组结构的散列表
        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
        // 见下文:
        mContentParent.saveHierarchyState(states);
        // 把视图树的状态保存结果放到Activity传入的Bundle中
        outState.putSparseParcelableArray(VIEWS_TAG, states);
        // 保存当前正获取到焦点的View的id
        final View focusedView = mContentParent.findFocus();
        if (focusedView != null && focusedView.getId() != View.NO_ID) {
            outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
        }
        // ...
        return outState;
    } 

mContentParent.saveHierarchyState(states),实现就是View啦:

// View.dispatchSaveInstanceState实现如下:
    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        // 只要view有id,并且没有关闭掉状态保存功能,就会被保存
        if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
            // 回调View的onSaveInstanceState实现
            // 在View.java中的实现其实就是返回一个空的Parcelable对象(和一些Autofill相关的逻辑)
            Parcelable state = onSaveInstanceState();
            if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                throw new IllegalStateException(
                        "Derived class did not call super.onSaveInstanceState()");
            }
            if (state != null) {
                // 在上面创建的稀松数组中,存放的key就是View的id。
                container.put(mID, state);
            }
        }
    } 

所以可以看到,对于View而言,其实就是保存自身的状态,而VierGroup则不难猜到,肯定是遍历子View进行状态保存:

 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        super.dispatchSaveInstanceState(container);
        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
            View c = children[i];
            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
                c.dispatchSaveInstanceState(container);
            }
        }
    } 

如此以来,视图树的状态就保存完了,我们可以看一下比如Switch这个开关组件的状态保存,因为Switch继承自CompoundButton,其并没有实现自己的onSaveInstanceState方法,在父类CompoundButton中:

 public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.checked = isChecked();// 保存按钮的checked选中态。
        return ss;
    } 

View的状态恢复:

那视图的状态保存完了,接下来就是考虑在恰当的时候进行恢复,还是以Switch开关这个控件为例子:

 @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        // 恢复是否被选中的状态。
        setChecked(ss.checked);
        requestLayout();
    } 

而在Activity进行Start的时候,会主动回调到相关逻辑:

-> Activity#handleStartActivity

-> Instrumentation#callActivityOnRestoreInstanceState

-> activity.performRestoreInstanceState

 final void performRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        // 回调onRestoreInstanceState
        onRestoreInstanceState(savedInstanceState);
        // 恢复Dialog的状态
        restoreManagedDialogs(savedInstanceState);
    } 

视图状态的保存开始自PhoneWindow,恢复自然也一样,在onRestoreInstanceState的默认实现中,先获取到之前保存的Window状态的Bundle对象,然后mWindow.restoreHierarchyState进行恢复,其实和序列化的过程很类似,状态的恢复其实就是状态保存过程的逆向,视图树状态的恢复关键代码:

mContentParent.restoreHierarchyState(savedStates); 

mContentParent是FrameLayout实例,其本身没有实现restoreHierarchyState,在View中:

-> View#restoreHierarchyState

-> View#dispatchRestoreInstanceState

 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        if (mID != NO_ID) {
            Parcelable state = container.get(mID);
            // 如果有当前View的状态
            if (state != null) {
                mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
                // 有的话调用onRestoreInstanceState
                onRestoreInstanceState(state);
            }
        }
    } 

onRestoreInstanceState在子View中的实现就如上面Switch的实现。

Fragment状态的保存与恢复:

Fragment状态保存:

在Activity的onSaveInstanceState中,我们看到了Fragment状态相关的保存逻辑:

-> mFragments.saveAllState()

-> mHost.mFragmentManager.saveAllState()

FragmentManagerImpl的实现如下:

 Parcelable saveAllState() {
        // 强制执行一些前置任务,比如提交的事务全部执行掉,以此保证状态是最新的。
        // 嗅到了一些不好的味道
        forcePostponedTransactions();
        endAnimatingAwayFragments();
        execPendingActions();

        mStateSaved = true;
        mSavedNonConfig = null;
        // 如果当前没有活跃状态的Fragment,就不保存
        if (mActive == null || mActive.size() <= 0) {
            return null;
        }
        // First collect all active fragments.
        int N = mActive.size();
        FragmentState[] active = new FragmentState[N];
        boolean haveFragments = false;
        for (int i=0; i<N; i++) {
            Fragment f = mActive.valueAt(i);
            if (f != null) {
                if (f.mIndex < 0) {
                    throwException(new IllegalStateException(
                            "Failure saving state: active " + f
                            + " has cleared index: " + f.mIndex));
                }
                haveFragments = true;
                FragmentState fs = new FragmentState(f);
                active[i] = fs;
                
                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                    // 保存Fragment对象的状态
                    // 在这其中主要做两件事,第一个是调用Fragment自身的onSave方法保存自身的状态
                    // 第二个是调用Fragment的mChildFragmentManager去递归保存子Fragment的状态。
                    fs.mSavedFragmentState = saveFragmentBasicState(f);
                    if (f.mTarget != null) {
                        // 保存Fragment的TargetFragment的在mActive中记录的索引。
                        if (f.mTarget.mIndex < 0) {
                            throwException(new IllegalStateException(
                                    "Failure saving state: " + f
                                    + " has target not in fragment manager: " + f.mTarget));
                        }
                        if (fs.mSavedFragmentState == null) {
                            fs.mSavedFragmentState = new Bundle();
                        }
                        putFragment(fs.mSavedFragmentState,
                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
                        if (f.mTargetRequestCode != 0) {
                            fs.mSavedFragmentState.putInt(
                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
                                    f.mTargetRequestCode);
                        }
                    }
                } else {
                    fs.mSavedFragmentState = f.mSavedFragmentState;
                }
                if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
                        + fs.mSavedFragmentState);
            }
        } 
        if (!haveFragments) {
            if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
            return null;
        }
        
        int[] added = null;
        BackStackState[] backStack = null;
        
        // Build list of currently added fragments.
        N = mAdded.size();
        if (N > 0) {
            added = new int[N];
            for (int i=0; i<N; i++) {
                added[i] = mAdded.get(i).mIndex;
                if (added[i] < 0) {
                    throwException(new IllegalStateException(
                            "Failure saving state: active " + mAdded.get(i)
                            + " has cleared index: " + added[i]));
                }
                if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
                        + ": " + mAdded.get(i));
            }
        }

        // 保存Fragment的回退栈的状态
        if (mBackStack != null) {
            N = mBackStack.size();
            if (N > 0) {
                backStack = new BackStackState[N];
                for (int i=0; i<N; i++) {
                    backStack[i] = new BackStackState(this, mBackStack.get(i));
                    if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
                            + ": " + mBackStack.get(i));
                }
            }
        }
        // 创建Fragment状态对象,并将上面收集到的所有的状态数据全部写入到fms中,然后交给Activity保存。
        FragmentManagerState fms = new FragmentManagerState();
        fms.mActive = active;
        fms.mAdded = added;
        fms.mBackStack = backStack;
        fms.mNextFragmentIndex = mNextFragmentIndex;
        if (mPrimaryNav != null) {
            fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
        }
        saveNonConfig();
        return fms;
    } 

其实从上面代码,尤其是活跃状态Fragment状态的保存那一块,也可以看出,Fragment的状态管理确实很复杂。。。

除了Activity中调用FM进行Fragment的状态保存,FragmentManager还提供了public方法: saveFragmentInstanceState(fragment), 可以对单个Fragment进行状态保存。

以及在FragmentManager的moveToState进行Fragment状态变更的时候,当Fragment的状态回退到ACTIVITY_CREATE之前,其实也就是初始化状态(Fragment执行onDestroy之前会置为这一状态)和不合法状态。

Fragment状态恢复:

按照Google的编码习惯,状态的恢复也一定是在FragmentManagerImpl中,并且api叫做restoreAllState,并且和上文的三种状态保存是对应的,API设计的很合理。

而Activity中对于Fragment的状态恢复入口在Acitvity的onCreate中:

 if (savedInstanceState != null) {
            // ...
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            // 调用到Fragment的restoreAllState方法。
            mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                    ? mLastNonConfigurationInstances.fragments : null);
        }
        mFragments.dispatchCreate(); 

restoreAllState方法主要分为一下几步:

  1. 先把受Activity重建影响的Fragment实例状态进行恢复。
  2. 把所有之前位于mActive中的Fragment实例进行重新创建实例和状态恢复。
  3. 更新Fragment的mTarget索引。
  4. 把上面搞出来的一堆Fragment添加到FragmentManager中的mAdded容器中。
  5. 如有需要的话,恢复Fragment的返回栈状态。

而对于外部主动使用saveFragmentInstanceState进行状态保存的方法,对应的恢复状态方法是Fragment的setInitialSavedState,这个方法其实就是把状态对象赋值给Fragment,而后FragmentManager执行到Fragment对应的声明周期的时候,会主动去把Fragment已经存在的状态对象传递给对应的方法比如onCreateView。

对于通过moveToState触发的状态恢复,会在Fragment的newState > Fragment.CREATED的时候,触发视图的恢复,而保存状态的Bundle也会在onCreateView,onViewCreated,onActivityCreated中传递给Fragment。以及通过FragmentLifecycleCallbacks暴露给外部。在之后是调用Fragment的restoreViewState,进行视图状态的恢复,开始的起点就是Fragment的mView字段,而这个字段,就是我们通过onCreateView返回的对象,当Fragment通过onCreateView完成Fragment视图的创建之后,fm会将其赋值给Fragment的mView字段。

Dialog状态的保存和恢复:

Dialog状态的保存:

看Activity.java中这一部分的源码的提交记录已经是2009年了,相关的逻辑其实就是调用当前Activity的所有Dialog实例,一次进行保存和恢复,相关的逻辑官方推荐是用DialogFragment来弹Dialog,不妨看下DialogFragment的实现(DialogFragment本身也是一个Fragment实例,通过FragmentManager进行管理,所以状态的保存和恢复和Fragment没什么太大的差异,只不过DialogFragment实现了自定义的onSaveInstanceState方法):

 public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mDialog != null) {
            // 调用当前DialogFragment关联的mDialog的onSaveInstanceState方法
            Bundle dialogState = mDialog.onSaveInstanceState();
            if (dialogState != null) {
                outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
            }
        }
        if (mStyle != STYLE_NORMAL) {
            outState.putInt(SAVED_STYLE, mStyle);
        }
        if (mTheme != 0) {
            outState.putInt(SAVED_THEME, mTheme);
        }
        if (!mCancelable) {
            outState.putBoolean(SAVED_CANCELABLE, mCancelable);
        }
        // 如果当前DialogFragment正在展示Dialog,要把这个标记位记录下来
        if (!mShowsDialog) {
            outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
        }
        if (mBackStackId != -1) {
            outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
        }
    } 

Dialog中的实现和其它组件相比比较简单,就是是否正在展示dialog的标记位保存和视图状态的保存:

 public @NonNull Bundle onSaveInstanceState() {
        Bundle bundle = new Bundle();
        // 保存当前Dialog是否正在展示态
        bundle.putBoolean(DIALOG_SHOWING_TAG, mShowing);
        if (mCreated) {
            // Dialog关联的PhoneWindow视图状态保存
            bundle.putBundle(DIALOG_HIERARCHY_TAG, mWindow.saveHierarchyState());
        }
        return bundle;
    } 

状态的恢复如下:

 public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG);
        if (dialogHierarchyState == null) {
            // dialog has never been shown, or onCreated, nothing to restore.
            return;
        }
        // 如果之前Dialog还没有执行到onCreate,执行一下onCreate。
        dispatchOnCreate(savedInstanceState);
        // 恢复Dialog关联的视图树状态。
        mWindow.restoreHierarchyState(dialogHierarchyState);
        // 如果之前Dialog是正在展示,这里重新show出来
        if (savedInstanceState.getBoolean(DIALOG_SHOWING_TAG)) {
            show();
        }
    } 


  

ull) {
            // dialog has never been shown, or onCreated, nothing to restore.
            return;
        }
        // 如果之前Dialog还没有执行到onCreate,执行一下onCreate。
        dispatchOnCreate(savedInstanceState);
        // 恢复Dialog关联的视图树状态。
        mWindow.restoreHierarchyState(dialogHierarchyState);
        // 如果之前Dialog是正在展示,这里重新show出来
        if (savedInstanceState.getBoolean(DIALOG_SHOWING_TAG)) {
            show();
        }
    } 

最后

在技术领域内,没有任何一门课程可以让你学完后一劳永逸,再好的课程也只能是“师傅领进门,修行靠个人”。“学无止境”这句话,在任何技术领域,都不只是良好的习惯,更是程序员和工程师们不被时代淘汰、获得更好机会和发展的必要前提。

**如果你觉得自己学习效率低,缺乏正确的指导,可以扫码,加入我们资源丰富,学习氛围浓厚的技术圈一起学习交流吧!


加入我们吧!群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值