查看原文
其他

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

牛晓伟 牛晓伟
2024-08-24

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

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

原理

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

保存
save-state

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

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

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

恢复
请添加图片描述

恢复这一过程,它是受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 小结

用一张时序图总结

请添加图片描述

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

  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 小结
请添加图片描述

到此恢复流程分析完毕,恢复数据可以在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进行交互),所以这时候是一个非常好的时机把数据保存起来。


继续滑动看下一个
牛晓伟
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存