Activity系列--Activity界面状态数据保存恢复-源码分析

在开始这节之前,我先介绍下我写源码分析类文章的方式.
首先源码分析类的文章,顾名思义就是从源码的角度来分析某一个知识点,我不希望我写的源码分析类的文章是这样的:整个篇幅或者一上来就是关于各种方法调用的分析,方法逻辑的分析,这种形式的文章会给人枯燥乏味感.
我希望以这样的方式来写这类文章:
首先是原理部分:这部分内容不会包含代码的分析,主要以图文的形式来对源码的整个流程进行梳理总结,不会涉及到细节,进而对相应的知识从整体上有一个了解.
其次是源码部分:这部分就是完全以源码的角度来分析,并且会涉及到细节.
最后是思考部分:这部分是我的一些思考,一些问题,一些能借鉴的东西.

Activity界面状态数据的保存是在onSaveInstanceState回调方法,界面状态数据的恢复是在onRestoreInstanceState回调方法。本节就从原理和源码两个角度来进行分析。

原理

Activity界面状态分为保存和恢复两个步骤,就按这俩步骤来介绍下原理。

保存
save_state.png

保存这一过程,它不受AMS(ActivityManagerService的简称,运行于系统进程)的控制,android把保存的自主权完全交给了app,app会根据不同的条件来决定是否要进行数据的保存。

若需要保存,它会在Activity进入stop状态之后或之前,它会调用Activity的onSaveInstanceState方法,在这个方法中会把保存这一操作告诉给Activity包含的所有views和fragments,你们有需要保存数据的吗?有的话就交上来。app进程把需要保存的数据收集起来后,就统一寄存在AMS,因此即使当前app进程被杀掉,它的一些需要保存的数据已经在AMS中保存下来了。

不同的view会根据自己的需求来决定是否需要保存数据(比如EditText会把正在输入的文本信息保存下来)

恢复
restore_state.png

恢复这一过程,它是受AMS控制的。由于低内存等异常原因导致app被杀掉,app再次重新创建的时候,AMS会把之前app保存的数据传递给相应的Activity,Activity的onRestoreInstanceState方法或者onCreate方法的Bundle类型的参数包含了传递过来的参数,Activity也会把view保存的数据分发给它包含的views,不同的view根据自己保存的数据,恢复自己的状态(比如EditText会把上次输入时候的文本恢复)

当然了恢复机制完全是针对 由于一些异常情况导致app进程被杀或者Activity需要重新启动的情况。用户正常的按返回键退出Activity或者正常退出应用,或者用户手动杀掉app进程(在最近任务中)这种情况是不会启动恢复机制的。

源码分析

源码分析着重介绍app进程端的,关于AMS端的会在后面逐步介绍(分析的源码版本是android 12)。我们从保存和恢复这两个步骤来分别进行分析。

1. 保存

保存的操作分为三部分:

  1. 决定是否保存
  2. 若需要保存,则收集需要保存的数据
  3. 收集的数据发送给AMS

config信息变化会导致保存操作,从一个Activity跳转到另外一个Activity或者点击home键回到桌面也会引起保存操作。
咱们分析从一个Activity跳转到另外一个Activity这种情况下的保存操作。

跳转到另外一个Activity时候,当前的Activity会进入stop状态,AMS会通知Activity进入stop,那就从这作为切入点

1.1 Activity进入stop流程
1.1.1 StopActivityItem的初始化

StopActivityItem的初始化代码如下:(位于StopActivityItem.java)

    public static StopActivityItem obtain(int configChanges) {
        StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class);
        if (instance == null) {
            instance = new StopActivityItem();
        }
        instance.mConfigChanges = configChanges;

        return instance;
    }

上面的代码AMS会调用,AMS初始化一个StopActivityItem后,最终会把它通过binder调用传入app进程(关于AMS与app进程的binder通信后面会介绍),StopActivityItem就是AMS用来告诉app的哪个Activity可以开始进入stop状态。当StopActivityItem的实例post到ui线程后,它的execute方法会被执行。

1.1.2 execute方法

execute方法代码如下:(StopActivityItem.java)

    @Override
    public void execute(ClientTransactionHandler client, ActivityClientRecord r,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
        //ActivityThread继承了ClientTransactionHandler,因此进入ActivityThread的handleStopActivity方法
        client.handleStopActivity(r, mConfigChanges, pendingActions,
                true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

从这个方法开始后面所有的流程都是运行在app进程的

StopActivityItem重写了ActivityLifecycleItem的execute,这方法是一个回调方法,在合适的时机会调用它。这个类还有一个postExecute方法,在整个stop流程执行完毕后,还会返回到这个方法,请记住它。

方法的参数介绍:
ClientTransactionHandler client:是一个抽象类,定义了一组接口,ActivityThread继承了它。
ActivityClientRecord r:封装了启动的Activity的很多信息。

这些都不属于咱们这节要讨论的内容,后面章节会详细介绍。

因为ActivityThread继承了ClientTransactionHandler,因此进入ActivityThread的handleStopActivity方法

1.1.3 handleStopActivity方法

对应代码:(位于ActivityThread.java)

    @Override
    public void handleStopActivity(ActivityClientRecord r, int configChanges,
            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
        r.activity.mConfigChangeFlags |= configChanges;

        final StopInfo stopInfo = new StopInfo();
        //在调用这个方法的时候,saveState的值为true,代表需要Activity进行状态保存
        performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest,
                reason);

        if (localLOGV) Slog.v(
            TAG, "Finishing stop of " + r + ": win=" + r.window);

        updateVisibility(r, false);

        // Make sure any pending writes are now committed.
        if (!r.isPreHoneycomb()) {
            QueuedWork.waitToFinish();
        }

        stopInfo.setActivity(r);
        stopInfo.setState(r.state);
        stopInfo.setPersistentState(r.persistentState);
        pendingActions.setStopInfo(stopInfo);
        mSomeActivitiesChanged = true;
    }

首先构造一个StopInfo stopInfo对象,它作为performStopActivityInner方法的参数,performStopActivityInner方法还有个参数saveState(代表是否要进行保存操作),当前的值为true

那就先就进入performStopActivityInner这个方法探探究竟,后面还会返回到这方法

1.1.4 performStopActivityInner方法

看下它的代码:(位于ActivityThread.java)

    private void performStopActivityInner(ActivityClientRecord r, StopInfo info,
            boolean saveState, boolean finalStateRequest, String reason) {
        ......省略代码

        // One must first be paused before stopped...
        //进入stop状态之前需要先进入pause状态
        performPauseActivityIfNeeded(r, reason);

        ......省略代码
        //调用callActivityOnStop方法
        callActivityOnStop(r, saveState, reason);
    }

上面代码的意思:
在Activity进入stop状态之前会先让Activity进入pause状态进而会调用Activity的onPause方法,接着调用callActivityOnStop方法

1.2 决定是否保存操作

callActivityOnStop方法中决定了是否要进行保存操作。

1.2.1 callActivityOnStop方法

看下它的关键代码:(位于ActivityThread.java)

private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
        // Before P onSaveInstanceState was called before onStop, starting with P it's
        // called after. Before Honeycomb state was always saved before onPause.
        //当前saveState为true,
        final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
                && !r.isPreHoneycomb();
        final boolean isPreP = r.isPreP();
        //在android P版本之前 在onStop方法之前执行onSaveInstanceState方法
        if (shouldSaveState && isPreP) {
            callActivityOnSaveInstanceState(r);
        }

        try {
            //开始调用Activity的onStop方法
            r.activity.performStop(r.mPreserveWindow, reason);
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
            if (!mInstrumentation.onException(r.activity, e)) {
                throw new RuntimeException(
                        "Unable to stop activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
            }
        }
        r.setState(ON_STOP);
        //在P版本以后,在onStop方法之后执行onSaveInstanceState方法
        if (shouldSaveState && !isPreP) {
            callActivityOnSaveInstanceState(r);
        }
    }

先来看下final boolean shouldSaveState这个值,它最后的值最终决定了是否要进行保存操作,它的值是由
saveState && !r.activity.mFinished && r.state == null && !r.isPreHoneycomb() 计算得到的

saveState:当前的值为true

r.activity.mFinished:当用户按了返回按键或者代码里面主动调用了finish方法(暂且称这种操作为主动操作),该值为true。那 !r.activity.mFinished 就代表着非主动操作的时候才有必要保存。

r.state:它指向了保存的数据,r.state会在ActivityThread的performResumeActivity方法中置为null(也就是Activity进入resume状态的时候)。r.state == null 就意味着只有Activity进入resume状态后才有必要保存

!r.isPreHoneycomb():android为Honeycomb这个版本的是,保存的操作是在pause状态的时候进行存储。非Honeycomb版本,则需要在Activity进入stop状态存储。

小结下上面代码: 在saveState为true,也就是调用者要求保存的前提下,需要当前的Activity 非主动操作,并且Activity进入过resume状态,并且是在android不是Honeycomb这个版本 的情况下才会进行保存操作。

或者可以这样理解:

  1. 用户点击了返回按键或者代码中主动调用了finish方法导致 需要销毁Activity,这种情况下是不需要保存操作的。这也很好理解,这都属于主动操作,为啥还有保存操作呢,保存这些数据没有任何意义并且还会浪费内存
  2. 启动新的Activity或者点击home进入桌面的时候 导致Activity进入stop状态,这些情况是需要保存操作的
  3. 当Activity没有进入过resume状态的时候(不能与用户进行交互的状态),这时候是完全没必要进行保存操作的。这也很好理解,用户都不能操作Activity何来保存一说呢。

当shouldSaveState为true,会调用callActivityOnSaveInstanceState方法

1.3 收集需要保存的数据

下面的分析开始进入收集保存数据的流程

1.3.1 callActivityOnSaveInstanceState方法

看下它的代码:(位于ActivityThread.java)

    private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
        r.state = new Bundle();
        r.state.setAllowFds(false);
        //需要在AndroidManifest配置activity的时候,配置android:persistableMode属性设置为非persistNever时候有效,Activity的信息会被持久化的disk上
        if (r.isPersistable()) {
            r.persistentState = new PersistableBundle();
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                    r.persistentState);
        } else {
            //咱们只关心下面的情况
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    }

保存的数据都会存放在r.state中

1.3.2 callActivityOnSaveInstanceState方法

它的代码:(Instrumentation.java)

    public void callActivityOnSaveInstanceState(@NonNull Activity activity,
            @NonNull Bundle outState) {
        activity.performSaveInstanceState(outState);
    }
1.3.3 performSaveInstanceState方法

代码如下:(Activity.java)

    final void performSaveInstanceState(@NonNull Bundle outState) {
        dispatchActivityPreSaveInstanceState(outState);
        //咱们只关注onSaveInstanceState方法
        onSaveInstanceState(outState);
        saveManagedDialogs(outState);
        mActivityTransitionState.saveState(outState);
        storeHasCurrentPermissionRequest(outState);
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
        dispatchActivityPostSaveInstanceState(outState);
    }
1.3.4 onSaveInstanceState方法

代码如下:(Activity.java)

    protected void onSaveInstanceState(@NonNull Bundle outState) {
        //把从mWindow.saveHierarchyState()获取的数据以WINDOW_HIERARCHY_TAG作为key放入outState中
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);

        //mFragments.saveAllState()获取的数据也同样会放入outState中,因此Fragments中也会做和Activity同样的事情,去收集需要保存的数据
        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);
    }

上面代码作用:

  1. 从mWindow中收集需要保存的数据放入outState中
  2. 从所有的fragments中收集数据放入outState中
  3. 分发save事件出去

fragment不属于咱们讨论的范畴,咱们只关注window相关的保存。mWindow它是PhoneWindow的实例,PhoneWindow是Window的子类,这个会在view系列介绍

1.3.5 saveHierarchyState方法

代码如下:(PhoneWindow.java)

    @Override
    public Bundle saveHierarchyState() {
        //new 一个outState,它会把数据收集起来,最终返回给Activity进行保存
        Bundle outState = new Bundle();
        //mContentParent为null就完全没必要收集,直接返回
        if (mContentParent == null) {
            return outState;
        }

        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
        //mContentParent可以理解为一个Activity中顶层的view,它包含了所有的content view
        mContentParent.saveHierarchyState(states);
        outState.putSparseParcelableArray(VIEWS_TAG, states);

        // Save the focused view ID.
        final View focusedView = mContentParent.findFocus();
        //如果焦点view不为null,并且它的id存在,则把是焦点的view的id保存起来
        if (focusedView != null && focusedView.getId() != View.NO_ID) {
            outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
        }

        ......省略代码

        return outState;
    }

上面代码作用:

  1. new一个Bundle outState,它会作为返回值返回给Activity
  2. mContentParent收集需要保存的数据,放入outState
  3. 找到焦点view,如果它存在id,则保存下来,以便下次恢复

咱们只关系view的保存,看下saveHierarchyState这方法

1.3.6 saveHierarchyState方法

它的代码:(View.java)

    public void saveHierarchyState(SparseArray<Parcelable> container) {
        dispatchSaveInstanceState(container);
    }

因为mContentParent是一个ViewGroup(ViewGroup是View的子类),saveHierarchyState方法位于View类

1.3.7 ViewGroup的dispatchSaveInstanceState方法

它的代码:(ViewGroup.java)

    @Override
    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];
            //遍历它的所有的子view,当子view没有设置PARENT_SAVE_DISABLED这个flag的时候,去保存数据
            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
                //若c是ViewGroup则进入ViewGroup的dispatchSaveInstanceState方法,否则进入View的dispatchSaveInstanceState方法
                c.dispatchSaveInstanceState(container);
            }
        }
    }

上面方法作用:
遍历它的所有的子view(因为ViewGroup包含了很多的子view),当子view没有设置PARENT_SAVE_DISABLED这个flag的时候,去保存数据

那来看下View类的dispatchSaveInstanceState方法

1.3.8 View的dispatchSaveInstanceState方法

代码如下:(View.java)

    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        //有一个非常关键的条件,view必须要有id,并且没有SAVE_DISABLED_MASK的flag
        if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
            //调用view的onSaveInstanceState方法,它会返回一个Parcelable
            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);
            }
        }
    }

如果一个view想要保存数据,一个很关键的条件就是它需要有id(id的设置可以在layout文件中使用android:id 进行设置,也可以使用代码方式设置),并且mViewFlags没有设置不能保存的flag,才可对view数据进行保存。

1.3.9 onSaveInstanceState方法

方法代码:(View.java)

    @Nullable protected Parcelable onSaveInstanceState() {
        mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
        if (mStartActivityRequestWho != null || isAutofilled()
                || mAutofillViewId > LAST_APP_AUTOFILL_ID) {
            BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);

            if (mStartActivityRequestWho != null) {
                state.mSavedData |= BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED;
            }

            if (isAutofilled()) {
                state.mSavedData |= BaseSavedState.IS_AUTOFILLED;
            }

            if (mAutofillViewId > LAST_APP_AUTOFILL_ID) {
                state.mSavedData |= BaseSavedState.AUTOFILL_ID;
            }

            state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
            state.mIsAutofilled = isAutofilled();
            state.mHideHighlight = hideAutofillHighlight();
            state.mAutofillViewId = mAutofillViewId;
            return state;
        }
        return BaseSavedState.EMPTY_STATE;
    }

上面方法是View类的默认实现,自定义的View可以根据自己的需求只需要重写onSaveInstanceState该方法,就可以把需要保存的数据收集起来。

1.4 收集的数据发送给AMS

当需要保存的数据都收集起来,放入r.state(r是ActivityClientRecord的实例)中后,就需要把数据发送给AMS。在回到handleStopActivity方法

1.4.1 handleStopActivity方法

代码如下:

    @Override
    public void handleStopActivity(ActivityClientRecord r, int configChanges,
            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
        r.activity.mConfigChangeFlags |= configChanges;

        final StopInfo stopInfo = new StopInfo();
        //在调用这个方法的时候,saveState的值为true,代表需要Activity进行状态保存
        performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest,
                reason);

        if (localLOGV) Slog.v(
            TAG, "Finishing stop of " + r + ": win=" + r.window);

        updateVisibility(r, false);

        // Make sure any pending writes are now committed.
        if (!r.isPreHoneycomb()) {
            QueuedWork.waitToFinish();
        }

        stopInfo.setActivity(r);
        stopInfo.setState(r.state);
        stopInfo.setPersistentState(r.persistentState);
        pendingActions.setStopInfo(stopInfo);
        mSomeActivitiesChanged = true;
    }

performStopActivityInner方法执行完毕后,会对StopInfo stopInfo进行初始化,其中 stopInfo.setState(r.state);代码中的r.state就是刚刚收集到的保存数据,设置到stopInfo中。
PendingTransactionActions pendingActions:pendingActions.setStopInfo(stopInfo)把stopInfo设置到pendingActions中。

handleStopActivity方法执行完后,会进入StopActivityItem的postExecute方法

1.4.2 postExecute方法

它的代码:(StopActivityItem.java)

    @Override
    public void postExecute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        client.reportStop(pendingActions);
    }

同样这个方法也是被动调用的,ActivityThread继承了ClientTransactionHandler,它实现了reportStop方法

1.4.3 reportStop方法

它的代码:(ActivityThread.java)

    @Override
    public void reportStop(PendingTransactionActions pendingActions) {
        //post到ui线程执行,pendingActions.getStopInfo()肯定是一个Runnable
        mH.post(pendingActions.getStopInfo());
    }

1.4.1 步为PendingTransactionActions pendingActions设置了stopinfo,那来看下StopInfo到底是一个什么?

1.4.4 StopInfo类

它的代码:(StopInfo.java)

public static class StopInfo implements Runnable{
        @Override
        public void run() {
            // Tell activity manager we have been stopped.
            try {
                if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity);
                // TODO(lifecycler): Use interface callback instead of AMS.
                ActivityClient.getInstance().activityStopped(
                        mActivity.token, mState, mPersistentState, mDescription);
            } catch (RuntimeException ex) {
                // Dump statistics about bundle to help developers debug
                final LogWriter writer = new LogWriter(Log.WARN, TAG);
                final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
                pw.println("Bundle stats:");
                Bundle.dumpStats(pw, mState);
                pw.println("PersistableBundle stats:");
                Bundle.dumpStats(pw, mPersistentState);

                if (ex.getCause() instanceof TransactionTooLargeException
                        && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
                    Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
                    return;
                }
                throw ex;
            }
        }
}

1.4.3 步在ui线程中post了一个Runnable,最终会进入它的run方法, 会调用ActivityClient.getInstance().activityStopped方法,其中mState参数就是保存的收集的数据

1.4.5 activityStopped方法

它的代码如下:(ActivityClient.java)

    public void activityStopped(IBinder token, Bundle state, PersistableBundle persistentState,
            CharSequence description) {
        try {
            getActivityClientController().activityStopped(token, state, persistentState,
                    description);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

getActivityClientController().activityStopped这是一个binder调用,会把token,收集的数据等传递给AMS。

到此收集的数据就发送给了AMS

1.5 小结

用一张时序图总结


save_state_sque.png

按以下步骤分析了保存的流程:

  1. AMS通知Activity进入stop状态
  2. 决定是否要保存数据
  3. 从Activity及它包含的views/fragments收集数据
  4. 把收集到的数据发送给AMS

对于像横竖屏切换这种导致Activity保存数据的过程有一个小小的差别,这种情况下收集的数据不会发送给AMS,一般会把收集的数据直接用于新创建的Activity。

2. 恢复

恢复的流程其实和保存基本相似,它的流程很简单,AMS若传递了收集的数据,就进行恢复操作,否则不进行

2.1 AMS通知Activity启动

像StopActivityItem一样,启动Activity也对应了一个类LaunchActivityItem,那我们就从它的初始化开始。

2.1.1 LaunchActivityItem初始化

它的代码:(LaunchActivityItem.java)

    public static LaunchActivityItem obtain(Intent intent, int ident, ActivityInfo info,
            Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo,
            String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
            PersistableBundle persistentState, List<ResultInfo> pendingResults,
            List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
            boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken,
            IActivityClientController activityClientController,
            FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
            boolean launchedFromBubble, IBinder taskFragmentToken) {
        LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
        if (instance == null) {
            instance = new LaunchActivityItem();
        }
        setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer,
                voiceInteractor, procState, state, persistentState, pendingResults,
                pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
                activityClientController, fixedRotationAdjustments, shareableActivityToken,
                launchedFromBubble, taskFragmentToken);

        return instance;
    }

AMS会调用obtain方法来初始化一个LaunchActivityItem,看它里面的关键参数:
ActivityInfo info:封装了要启动的是哪个Activity以及相关信息
Bundle state:AMS保存的从app收集上来的数据,也就是保存环节 传递给AMS的数据,这不又发给app进程的。

上面的参数会保存在LaunchActivityItem instance中。

LaunchActivityItem通过binder传递到app进程后,会被post到ui线程执行,它的execute方法会被执行

2.1.2 execute方法

它的代码:(LaunchActivityItem.java)

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
                        PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
                client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
                mLaunchedFromBubble, mTaskFragmentToken);
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

从这个方法开始,后面的方法都运行与app进程的ui线程中。各种参数会构造一个ActivityClientRecord r,它里面的mState就是存放了保存的数据,因为ActivityThread继承了ClientTransactionHandler,进入它的
handleLaunchActivity方法

2.1.3 handleLaunchActivity方法

它的代码:(ActivityThread.java)

    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        
        ......省略代码

        final Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfigurationController.getConfiguration());
            reportSizeConfigurations(r);
            //activity没有主动销毁并且pendingActions不为null
            if (!r.activity.mFinished && pendingActions != null) {
                pendingActions.setOldState(r.state);
                //下面这个值设为true 很关键,如果不为true后面的恢复操作不会执行
                pendingActions.setRestoreInstanceState(true);
                pendingActions.setCallOnPostCreate(true);
            }
        } else {
            // If there was an error, for any reason, tell the activity manager to stop us.
            ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED,
                    null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        }
        ......省略代码

        return a;
    }

上面方法调有performLaunchActivity方法。
pendingActions.setRestoreInstanceState(true)这行代码很关键,如果不为true,后面onRestoreInstanceState的恢复流程就不会执行

2.1.4 performLaunchActivity方法

它的代码:(ActivityThread.java)

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //拿到ActivityInfo信息
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            //下面代码主要是通过反射来生成一个Activity实例
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
                    appContext.getAttributionSource());
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            
            ......省略代码

            if (activity != null) {
                ......省略代码
                //若是持久化了,则调用这
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    //最终会调用到Activity的onCreate方法,r.state就是AMS传递的保存的数据
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                ......省略代码
            }
            r.setState(ON_CREATE);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }

上面的代码最终会调用到Activity的onCreate方法,onCreate方法的参数Bundle savedInstanceState就是代表AMS传递的保存的数据

2.1.5 小结

AMS会通知app启动相应的Activity,并且会把启动的activity信息,及上次保存的数据等信息放在ActivityClientRecord对象中,当Activity的onCreate方法被调用的时候会把保存数据传递过来。并且还做了一个工作:pendingActions.setRestoreInstanceState(true);后面的onRestoreInstanceState恢复操作会用到它。

上面的流程分析了进入Activity的onCreate方法的过程,恢复的流程可以在onCreate方法中进行(不建议在onCreate中进行恢复)。恢复操作也可以在onRestoreInstanceState方法中,这也是我们接下来要重点分析的.

onRestoreInstanceState恢复是在Activity进入start状态后调用的,因此从这作为入口,

2.2 Activity进入start状态,并进行恢复数据的分发
2.2.1 handleStartActivity方法

它的代码:(ActivityThread.java)

@Override
    public void handleStartActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, ActivityOptions activityOptions) {
        final Activity activity = r.activity;
        ......省略代码

        //调用Activity的onStart方法
        activity.performStart("handleStartActivity");
        r.setState(ON_START);
        //pendingActions 为null则不需要进行恢复等工作
        if (pendingActions == null) {
            // No more work to do.
            return;
        }

        // Restore instance state
        //进入恢复工作,pendingActions.shouldRestoreInstanceState()这个值在launch的时候设置为true了
        if (pendingActions.shouldRestoreInstanceState()) {
            if (r.isPersistable()) {
                if (r.state != null || r.persistentState != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                            r.persistentState);
                }
            } else if (r.state != null) {
                //r.state代表保存的数据,存在则开始进入callActivityOnRestoreInstanceState方法
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
            }
        }

        ......省略代码
    }

上面方法会通知Activity进入start状态,并且执行它的onStart方法。
pendingActions.shouldRestoreInstanceState()为true并且r.state不为null则进入callActivityOnRestoreInstanceState方法

2.2.2 callActivityOnRestoreInstanceState方法

它的代码:(Instrumentation.java)

    public void callActivityOnRestoreInstanceState(@NonNull Activity activity,
            @NonNull Bundle savedInstanceState) {
        activity.performRestoreInstanceState(savedInstanceState);
    }
2.2.3 onPostCreate方法

它的代码:(Activity.java)

    final void performRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        onRestoreInstanceState(savedInstanceState);
        restoreManagedDialogs(savedInstanceState);
    }

2.2.4 onRestoreInstanceState方法

它的代码:(Activity.java)

    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        if (mWindow != null) {
            //若保存了WINDOW_HIERARCHY_TAG,则进行恢复
            Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
            if (windowState != null) {
                mWindow.restoreHierarchyState(windowState);
            }
        }
    }
2.2.5 restoreHierarchyState方法

它的代码:(PhoneWindow.java)

    @Override
    public void restoreHierarchyState(Bundle savedInstanceState) {
        if (mContentParent == null) {
            return;
        }

        SparseArray<Parcelable> savedStates
                = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
        if (savedStates != null) {
            mContentParent.restoreHierarchyState(savedStates);
        }

        // restore the focused view
        int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
        if (focusedViewId != View.NO_ID) {
            View needsFocus = mContentParent.findViewById(focusedViewId);
            if (needsFocus != null) {
                needsFocus.requestFocus();
            } else {
                Log.w(TAG,
                        "Previously focused view reported id " + focusedViewId
                                + " during save, but can't be found during restore.");
            }
        }

        ......省略代码
    }

进入mContentParent.restoreHierarchyState方法

2.2.6 restoreHierarchyState方法

它的代码:(View.java)

    public void restoreHierarchyState(SparseArray<Parcelable> container) {
        dispatchRestoreInstanceState(container);
    }
2.2.7 ViewGroup的dispatchRestoreInstanceState方法

因为mContentParent是一个ViewGroup,因此进入ViewGroup的方法
它的代码:(ViewGroup.java)

    @Override
    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        super.dispatchRestoreInstanceState(container);
        final int count = mChildrenCount;
        final View[] children = mChildren;
        //遍历它包含的所有的子view
        for (int i = 0; i < count; i++) {
            View c = children[i];
            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
                c.dispatchRestoreInstanceState(container);
            }
        }
    }
2.2.8 View的dispatchRestoreInstanceState方法

它的代码:(View.java)

    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        //必须有id,否则不进恢复
        if (mID != NO_ID) {
            Parcelable state = container.get(mID);
            if (state != null) {
                // Log.i("View", "Restoreing #" + Integer.toHexString(mID)
                // + ": " + state);
                mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
                //调用onRestoreInstanceState方法开始恢复操作
                onRestoreInstanceState(state);
                if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                    throw new IllegalStateException(
                            "Derived class did not call super.onRestoreInstanceState()");
                }
            }
        }
    }   
2.2.9 onRestoreInstanceState方法

它的代码:(View.java)

    protected void onRestoreInstanceState(Parcelable state) {
        mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
        if (state != null && !(state instanceof AbsSavedState)) {
            throw new IllegalArgumentException("Wrong state class, expecting View State but "
                    + "received " + state.getClass().toString() + " instead. This usually happens "
                    + "when two views of different type have the same id in the same hierarchy. "
                    + "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "
                    + "other views do not use the same id.");
        }
        if (state != null && state instanceof BaseSavedState) {
            BaseSavedState baseState = (BaseSavedState) state;

            if ((baseState.mSavedData & BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED) != 0) {
                mStartActivityRequestWho = baseState.mStartActivityRequestWhoSaved;
            }
            if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) {
                setAutofilled(baseState.mIsAutofilled, baseState.mHideHighlight);
            }
            if ((baseState.mSavedData & BaseSavedState.AUTOFILL_ID) != 0) {
                // It can happen that views have the same view id and the restoration path will not
                // be able to distinguish between them. The autofill id needs to be unique though.
                // Hence prevent the same autofill view id from being restored multiple times.
                ((BaseSavedState) state).mSavedData &= ~BaseSavedState.AUTOFILL_ID;

                if ((mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) != 0) {
                    // Ignore when view already set it through setAutofillId();
                    if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.DEBUG)) {
                        Log.d(AUTOFILL_LOG_TAG, "onRestoreInstanceState(): not setting autofillId "
                                + "to " + baseState.mAutofillViewId + " because view explicitly set"
                                + " it to " + mAutofillId);
                    }
                } else {
                    mAutofillViewId = baseState.mAutofillViewId;
                    mAutofillId = null; // will be set on demand by getAutofillId()
                }
            }
        }
    }

上面方法是默认实现,如自定义view需要恢复操作,则需要重写该方法。

2.3 小结
restore_state_sque.png

到此恢复流程分析完毕,恢复数据可以在onCreate方法中进行(但是不建议在这操作),最好是onRestoreInstanceState方法中进行。

onRestoreInstanceState方法会在onStart方法执行完毕后调用,这个调用是有前提条件的:首先必须是Activity第一次创建的时候也就是执行了onCreate方法后,其次AMS传递了保存的数据;否则不会被调用。

对于像横竖屏切换这种导致Activity保存数据的过程有一个小小的差别,这种情况下收集的数据不会发送给AMS,一般会把收集的数据直接用于新创建的Activity。

总结

Activity界面状态保存恢复这机制,android底层封装了一套完美的框架,让我们不需要关心底层的细节,让我们使用的时候能有多简单就有多简单,比如我们想保存和恢复Activity层面的数据,那就重写Activity的onSaveInstanceState和onRestoreInstanceState方法,比如我们想对自定义view,进行保存和恢复操作,同样也只需要重写onSaveInstanceState和onRestoreInstanceState方法即可。这就是框架的魅力所在吧。

思考

为啥不能保存大数据?
我个人认为是从两方面考虑:一方面是app进程与系统进程进行通信用的是binder,而binder对传输的数据有大小限制。如果想保存和恢复大数据就需要使用到ViewModel。另外一方面是每个系统都要求系统保存大量的数据的话,那系统的压力就非常大了。

模板方法
在搭建保存恢复这机制这套框架的时候用到了模板方法设计模式。
BaseClientRequest定义了三个方法:preExecute,execute,postExecute。它的子类StopActivityItem和LaunchActivityItem都是它的子类,根据自己的需求实现了相应的方法。preExecute,execute,postExecute这三个方法什么时候被调用,子类完全不需要关心。
还有Activity/Viiew的onSaveInstanceState和onRestoreInstanceState方法,它们啥时候被调用,子类不需要关心,只需要把细节实现了就行。

为啥保存数据的所有权交给app?
首先站在系统的角度考虑,我系统给app提供了保存数据的功能,但是啥时候应该保存,保存哪些数据等等这些细节系统不关心,它也关心不过来。所以把保存的所有权交给app,系统的压力也少了很多。

为啥在Activity进入stop状态的时候保存数据?
既然要以Activity为单位来保持数据,那总得选一个合适的时机,在Activity处于create,start,resume状态的时候都不行,因为这时候的界面数据是不稳定的,并且尤其处于resume状态,进程处于前台,几乎不会被杀掉。
Activity进入stop状态后,它所在的进程变为后台不可见进程,这时候的Activity被杀掉的风险是比较大的,并且这时候Activity要保存的数据状态处于稳定态(因为进入stop用户也不可能与Activity进行交互),所以这时候是一个非常好的时机把数据保存起来。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容