浅谈APP的回收和重启机制

前言

我们在打开APP以后长期锁屏或者将APP放置后台,然后再点击APP图标或者任务栏点击指定的APP,重新打开APP时,会出现一段时间的白屏,那么这个时候其实APP曾经就被回收了,然后随着我们重新打开而重启了整个进程。


在讲这个机制之前我们先思考几个问题,然后再带着这几个问题再去深入源码:

  1. app是如何被干掉
  2. app被干掉以后,做了哪些处理
  3. 启动app进程的时候是怎么检测到app之前被干掉,从而重建APP
  4. 系统是如何保存现场的,保存在哪里
  5. 系统是如何恢复现场的,从哪里恢复

不过对于首个问题,我们这片文章不做说明,因为这涉及到linux kernel层杀死进程的机制,这一套机制花费的篇幅会比较长,以后有机会单独开篇章讲。这里只需要记住一旦linux决定要杀掉执行进程的时候会向AMS发送一个通知.


那么下面开始第二个问题:app被干掉以后,做了哪些处理
我们知道ActivityThread的main方法是一个APP进程的入口,在main方法中会做一件非常重要的事(关乎到APP被回收和重启)。

ActivityThread.main()->会new出ActivityThread对象->ActivityThread 对象调用attach方法->获取AMS->调用AMS.attachApplication->调用AMS.attachApplicationLocked->执行thread.asBinder().linkToDeath(adr, 0);

    private final class AppDeathRecipient implements IBinder.DeathRecipient {
        final ProcessRecord mApp;
        final int mPid;
        final IApplicationThread mAppThread;

        AppDeathRecipient(ProcessRecord app, int pid,
                IApplicationThread thread) {
            if (DEBUG_ALL) Slog.v(
                TAG, "New death recipient " + this
                 + " for thread " + thread.asBinder());
            mApp = app;
            mPid = pid;
            mAppThread = thread;
        }

        @Override
        public void binderDied() {
            if (DEBUG_ALL) Slog.v(
                TAG, "Death received in " + this
                + " for thread " + mAppThread.asBinder());
            synchronized(ActivityManagerService.this) {
                appDiedLocked(mApp, mPid, mAppThread, true);
            }
        }
    }

AppDeathRecipient就是监听APP被干掉的工具,当APP被干掉的时候,AppDeathRecipient的binderDied方法触发,appDiedLocked就是APP被杀掉以后通知的入口。那么下面我们来看下当APP被杀掉以后,系统做了什么处理:

appDiedLocked

那么我们就从这个方法开始说起:

    @GuardedBy("this")
    final void appDiedLocked(ProcessRecord app) {
       appDiedLocked(app, app.pid, app.thread, false);
    }

    @GuardedBy("this")
    final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
            boolean fromBinderDied) {
        // First check if this ProcessRecord is actually active for the pid.
        synchronized (mPidsSelfLocked) {
            ProcessRecord curProc = mPidsSelfLocked.get(pid);
            if (curProc != app) {
                Slog.w(TAG, "Spurious death for " + app + ", curProc for " + pid + ": " + curProc);
                return;
            }
        }

        BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
        synchronized (stats) {
            stats.noteProcessDiedLocked(app.info.uid, pid);
        }

        if (!app.killed) {
            if (!fromBinderDied) {
                killProcessQuiet(pid);
            }
            ProcessList.killProcessGroup(app.uid, pid);
            app.killed = true;
        }

        // Clean up already done if the process has been re-started.
        if (app.pid == pid && app.thread != null &&
                app.thread.asBinder() == thread.asBinder()) {
            boolean doLowMem = app.getActiveInstrumentation() == null;
            boolean doOomAdj = doLowMem;
            if (!app.killedByAm) {
                reportUidInfoMessageLocked(TAG,
                        "Process " + app.processName + " (pid " + pid + ") has died: "
                                + ProcessList.makeOomAdjString(app.setAdj, true) + " "
                                + ProcessList.makeProcStateString(app.setProcState), app.info.uid);
                mAllowLowerMemLevel = true;
            } else {
                // Note that we always want to do oom adj to update our state with the
                // new number of procs.
                mAllowLowerMemLevel = false;
                doLowMem = false;
            }
            EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName,
                    app.setAdj, app.setProcState);
            if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder());
            handleAppDiedLocked(app, false, true);

            if (doOomAdj) {
                updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
            }
            if (doLowMem) {
                doLowMemReportIfNeededLocked(app);
            }
        } else if (app.pid != pid) {
            // A new process has already been started.
            reportUidInfoMessageLocked(TAG,
                    "Process " + app.processName + " (pid " + pid
                            + ") has died and restarted (pid " + app.pid + ").", app.info.uid);
            EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName);
        } else if (DEBUG_PROCESSES) {
            Slog.d(TAG_PROCESSES, "Received spurious death notification for thread "
                    + thread.asBinder());
        }

        // On the device which doesn't have Cgroup, log LmkStateChanged which is used as a signal
        // for pulling memory stats of other running processes when this process died.
        if (!hasMemcg()) {
            StatsLog.write(StatsLog.APP_DIED, SystemClock.elapsedRealtime());
        }
    }

这里首先调用了handleAppDiedLocked方法,那么我们看下handleAppDiedLocked做了什么操作:

   /**
     * Main function for removing an existing process from the activity manager
     * as a result of that process going away.  Clears out all connections
     * to the process.
     */
    @GuardedBy("this")
    final void handleAppDiedLocked(ProcessRecord app,
            boolean restarting, boolean allowRestart) {
        int pid = app.pid;
        boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
                false /*replacingPid*/);
        if (!kept && !restarting) {
            removeLruProcessLocked(app);
            if (pid > 0) {
                ProcessList.remove(pid);
            }
        }

        if (mProfileData.getProfileProc() == app) {
            clearProfilerLocked();
        }

        mAtmInternal.handleAppDied(app.getWindowProcessController(), restarting, () -> {
            Slog.w(TAG, "Crash of app " + app.processName
                    + " running instrumentation " + app.getActiveInstrumentation().mClass);
            Bundle info = new Bundle();
            info.putString("shortMsg", "Process crashed.");
            finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
        });
    }

首先调用cleanUpApplicationRecordLocked方法,这个方法主要是将弹出的crash窗口,ANR窗口以及debug提示窗口关闭,清理providers,receivers,service相关的信息,以及在清理过程中判断当前进程是否要重启。而返回的kept如果为true,那么代表当前APP正在重启,false就可以直接干掉APP。
接下来会调用mAtmInternal.handleAppDied方法。mAtmInternal是一个ActivityTaskManagerInternal抽象类对象,这里是LocalService实现类,这个是ATMS的内部类这个方法将是我们APP被进程干掉以后的主要处理逻辑,那么我们继续往下走:

LocalService.java

       @HotPath(caller = HotPath.PROCESS_CHANGE)
        @Override
        public void handleAppDied(WindowProcessController wpc, boolean restarting,
                Runnable finishInstrumentationCallback) {
            synchronized (mGlobalLockWithoutBoost) {
                // Remove this application's activities from active lists.
                boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc);

                wpc.clearRecentTasks();
                wpc.clearActivities();

                if (wpc.isInstrumenting()) {
                    finishInstrumentationCallback.run();
                }

                if (!restarting && hasVisibleActivities) {
                    mWindowManager.deferSurfaceLayout();
                    try {
                        if (!mRootActivityContainer.resumeFocusedStacksTopActivities()) {
                            // If there was nothing to resume, and we are not already restarting
                            // this process, but there is a visible activity that is hosted by the
                            // process...then make sure all visible activities are running, taking
                            // care of restarting this process.
                            mRootActivityContainer.ensureActivitiesVisible(null, 0,
                                    !PRESERVE_WINDOWS);
                        }
                    } finally {
                        mWindowManager.continueSurfaceLayout();
                    }
                }
            }
        }

看第一行代码的注释,意思就是说从当前活动的列表中移除应用所有的activity,所以这就是那个核心方法:

RootActivityContainer.java

    boolean handleAppDied(WindowProcessController app) {
        boolean hasVisibleActivities = false;
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = display.getChildAt(stackNdx);
                hasVisibleActivities |= stack.handleAppDiedLocked(app);
            }
        }
        return hasVisibleActivities;
    }

最终调用了ActivityStack的handleAppDiedLocked方法

ActivityStack.java

    /**
     * Reset local parameters because an app's activity died.
     * @param app The app of the activity that died.
     * @return result from removeHistoryRecordsForAppLocked.
     */
    boolean handleAppDiedLocked(WindowProcessController app) {
        if (mPausingActivity != null && mPausingActivity.app == app) {
            if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
                    "App died while pausing: " + mPausingActivity);
            mPausingActivity = null;
        }
        if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }

        return removeHistoryRecordsForAppLocked(app);
    }


    private boolean removeHistoryRecordsForAppLocked(WindowProcessController app) {
        removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
        removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
                "mStoppingActivities");
        removeHistoryRecordsForAppLocked(mStackSupervisor.mGoingToSleepActivities, app,
                "mGoingToSleepActivities");
        removeHistoryRecordsForAppLocked(mStackSupervisor.mFinishingActivities, app,
                "mFinishingActivities");

        final boolean isProcessRemoved = app.isRemoved();
        if (isProcessRemoved) {
            // The package of the died process should be force-stopped, so make its activities as
            // finishing to prevent the process from being started again if the next top (or being
            // visible) activity also resides in the same process.
            app.makeFinishingForProcessRemoved();
        }

        boolean hasVisibleActivities = false;

        // Clean out the history list.
        int i = numActivities();
        if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                "Removing app " + app + " from history with " + i + " entries");
        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
            mTmpActivities.clear();
            mTmpActivities.addAll(activities);

            while (!mTmpActivities.isEmpty()) {
                final int targetIndex = mTmpActivities.size() - 1;
                final ActivityRecord r = mTmpActivities.remove(targetIndex);
                if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                        "Record #" + targetIndex + " " + r + ": app=" + r.app);

                if (r.app == app) {
                    if (r.visible) {
                        hasVisibleActivities = true;
                    }
                    final boolean remove;
                    if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
                            || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
                            && r.launchCount < 3 && !r.finishing) {
                        // If the process crashed during a resize, always try to relaunch it, unless
                        // it has failed more than twice. Skip activities that's already finishing
                        // cleanly by itself.
                        remove = false;
                    } else if ((!r.haveState && !r.stateNotNeeded
                            && !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) {
                        // Don't currently have state for the activity, or
                        // it is finishing -- always remove it.
                        remove = true;
                    } else if (!r.visible && r.launchCount > 2 &&
                            r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
                        // We have launched this activity too many times since it was
                        // able to run, so give up and remove it.
                        // (Note if the activity is visible, we don't remove the record.
                        // We leave the dead window on the screen but the process will
                        // not be restarted unless user explicitly tap on it.)
                        remove = true;
                    } else {
                        // The process may be gone, but the activity lives on!
                        remove = false;
                    }
                    if (remove) {
                        if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
                                "Removing activity " + r + " from stack at " + i
                                + ": haveState=" + r.haveState
                                + " stateNotNeeded=" + r.stateNotNeeded
                                + " finishing=" + r.finishing
                                + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
                        if (!r.finishing || isProcessRemoved) {
                            Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
                            EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
                                    r.mUserId, System.identityHashCode(r),
                                    r.getTaskRecord().taskId, r.shortComponentName,
                                    "proc died without state saved");
                        }
                    } else {
                        // We have the current state for this activity, so
                        // it can be restarted later when needed.
                        if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
                        if (DEBUG_APP) Slog.v(TAG_APP,
                                "Clearing app during removeHistory for activity " + r);
                        r.app = null;
                        // Set nowVisible to previous visible state. If the app was visible while
                        // it died, we leave the dead window on screen so it's basically visible.
                        // This is needed when user later tap on the dead window, we need to stop
                        // other apps when user transfers focus to the restarted activity.
                        r.nowVisible = r.visible;
                        if (!r.haveState) {
                            if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
                                    "App died, clearing saved state of " + r);
                            r.icicle = null;
                        }
                    }
                    cleanUpActivityLocked(r, true, true);
                    if (remove) {
                        removeActivityFromHistoryLocked(r, "appDied");
                    }
                }
            }
        }

        return hasVisibleActivities;
    }

这个方法主要将r.app设置为null。那么这个有什么作用呢?后面在做解释,到这里APP被杀死的之后的处理都做完了,那么我们接下来就要看如何重启了。

这里做个总结:

  1. 清掉对应的BroadcastReceiver,Service以及Provider相关的信息
  2. 关闭所有的系统弹出的弹窗(ANR弹窗,crash弹窗以及Debug弹窗)
  3. 尝试重启APP进程
  4. 然后并不会移除对应的ActivityRecord对象,并且将ActivityRecord的WindowProcessController对象置空。
App被回收时的处理.jpg

第三个问题:启动app进程的时候是怎么检测到app之前被干掉,从而重建APP
我们重新打开一个APP有两种选择:一种是点击Launcher界面的app图标启动,另一种就是打开任务页面,点击对应要重启的app。这里我们就以第一种举例吧,当点击APP图标的时候,APP是怎么启动的呢?这个时候我们先进入LauncherActivity看下吧。

        @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        
        mPackageManager = getPackageManager();

        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
            requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
            setProgressBarIndeterminateVisibility(true);
        }
        onSetContentView();

        mIconResizer = new IconResizer();
        
        mIntent = new Intent(getTargetIntent());
        mIntent.setComponent(null);
        mAdapter = new ActivityAdapter(mIconResizer);

        setListAdapter(mAdapter);
        getListView().setTextFilterEnabled(true);

        updateAlertTitle();
        updateButtonText();

        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
            setProgressBarIndeterminateVisibility(false);
        }
    }
    /**
     * Adapter which shows the set of activities that can be performed for a given intent.
     */
    private class ActivityAdapter extends BaseAdapter implements Filterable {
    }

LauncherActivity代码量不多,一眼就看到了这个Adapter,看注释知道这个应该就是桌面列表的适配器了,在onCreate的地方进行初始化,然后桌面其实就是一个ListView。接下来看下每个APP图标的点击事件:

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Intent intent = intentForPosition(position);
        startActivity(intent);
    }

LauncherActivity由于继承了ListActivity,所以它本身实现了一个onListItemClick方法,这个方法就是item点击的方法(即每个图标点击以后的处理方法),是个比较简单的启动Activity的流程。这里没啥特殊的地方。接下来就简要过一遍Activity的启动流程了。

  • Activity#startActivity
  • Activity#startActivityForResult
  • Instrumentation#execStartActivity()
  • ActivityManagerService#startActivity()
  • ActivityTaskManagerService#startActivity()
  • ActivityTaskManagerService#startActivityAsUser()
  • ActivityStarter#execute()
  • ActivityStarter#startActivityMayWait()
  • ActivityStarter#startActivity()
  • ActivityStarter#startActivityUnchecked()
  • RootActivityContainer#startActivityLocked()
  • ActivityStack#resumeTopActivityUncheckedLocked()
  • ActivityStack#resumeTopActivityInnerLocked()

上面我们重点关注下最后一步的操作:


    @GuardedBy("mService")
    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        。。。代码省略。。。
        // Find the next top-most activity to resume in this stack that is not finishing and is
        // focusable. If it is not focusable, we will fall into the case below to resume the
        // top activity in the next focusable task.
        ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);

        final boolean hasRunningActivity = next != null;

        // TODO: Maybe this entire condition can get removed?
        if (hasRunningActivity && !isAttached()) {
            return false;
        }

        。。。代码省略。。。
        

        if (next.attachedToProcess()) {
            。。。代码省略。。。
        } else {
            // Whoops, need to restart this activity!
            if (!next.hasBeenLaunched) {
                next.hasBeenLaunched = true;
            } else {
                if (SHOW_APP_STARTING_PREVIEW) {
                    next.showStartingWindow(null /* prev */, false /* newTask */,
                            false /* taskSwich */);
                }
                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
            }
            if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
            mStackSupervisor.startSpecificActivityLocked(next, true, true);
        }
        return true;
    }

final class ActivityRecord extends ConfigurationContainer {
    boolean hasProcess() {
        return app != null;
    }

    boolean attachedToProcess() {
        return hasProcess() && app.hasThread();
    }
}

next是ActivityRecord对象,next就是接下来将要启动的页面,判断attachedToProcess()为false的时候即app==null的情况下,会调用mStackSupervisor.startSpecificActivityLocked,而startSpecificActivityLocked就会进行Activity的重建。所以联系到之前收到进程被回收通知时的处理,可以得出一个结论就是对于非主动结束Activity生命周期的,AMS是不会清理掉对应的ActivityRecord的app对象的。换句话说app是否为null,是AMS决定是否重启Activity的依据。

   void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
        // 当APP被回收了,进程被杀掉以后,这个wpc必然是为空的
        final WindowProcessController wpc =
                mService.getProcessController(r.processName, r.info.applicationInfo.uid);

        boolean knownToBeDead = false;
        //所以当APP回收的时候不会执行realStartActivityLocked方法
        if (wpc != null && wpc.hasThread()) {
            try {
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
            knownToBeDead = true;
        }

        // Suppress transition until the new activity becomes ready, otherwise the keyguard can
        // appear for a short amount of time before the new process with the new activity had the
        // ability to set its showWhenLocked flags.
        if (getKeyguardController().isKeyguardLocked()) {
            r.notifyUnknownVisibilityLaunched();
        }

        try {
            if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
                        + r.processName);
            }
            //发送一个Message启动APP进程的message(这里涉及到了启动APP进程的流程,以后有时间单独开一篇讲)
            final Message msg = PooledLambda.obtainMessage(
                    ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
                    r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
            mService.mH.sendMessage(msg);
        } finally {
            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

至此APP重启流程也是告一段落。

主要是在resumeTopActivityInnerLocked的时候通过判断对应ActivityRecord的WindowProcessController对象来判断是否需要重启进程

App重建.jpg

第四个问题:APP是如何保存现场,保存在哪里
这里再不看源码的情况下,大胆猜想一下会是怎么做的呢?APP进程最后一旦被杀掉,那么所有的资源都会被释放掉,所以现场肯定不能保存在客户端,那么唯一的可能就是在服务端了。既然这里涉及到了服务端和客户端之间的数据交互。所以在回答这个问题之前,先来看下服务端和客户端之间到底是如何进行数据交互的。

由于篇幅有限,Activity的主要启动流程这里不做详细介绍。我们直接从ActivityStarter的startActivity方法:

    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
            SafeActivityOptions options,
            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
        ...代码省略...

        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
                resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
                mSupervisor, checkedOptions, sourceRecord);
        ...代码省略...

        final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);
        mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);
        return res;
    }

关注重点,在这个方法里面new出了一个ActivityRecord对象(ActivityRecord对象就是服务端保存Activity的所有信息)。在ActivityRecord里面初始化了一个Token,这个Token对于每一个ActivityRecord都是唯一的,后面所有地方都会通过这个token来进行校验。
接着经过一系列的调用到ActivityStackSupervisor的realStartActivityLocked方法

    boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
            boolean andResume, boolean checkConfig) throws RemoteException {

        ...代码省略...

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


                // Create activity launch transaction.
                final ClientTransaction clientTransaction = ClientTransaction.obtain(
                        proc.getThread(), r.appToken);

                final DisplayContent dc = r.getDisplay().mDisplayContent;
                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                        System.identityHashCode(r), r.info,
                        // TODO: Have this take the merged configuration instead of separate global
                        // and override configs.
                        mergedConfiguration.getGlobalConfiguration(),
                        mergedConfiguration.getOverrideConfiguration(), r.compat,
                        r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                        r.icicle, r.persistentState, results, newIntents,
                        dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                                r.assistToken));

                // Set desired final state.
                final ActivityLifecycleItem lifecycleItem;
                if (andResume) {
                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
                } else {
                    lifecycleItem = PauseActivityItem.obtain();
                }
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

                // Schedule transaction.
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);
                ...代码省略...

            } catch (RemoteException e) {
                if (r.launchFailed) {
                    // This is the second time we failed -- finish activity and give up.
                    Slog.e(TAG, "Second failure launching "
                            + r.intent.getComponent().flattenToShortString() + ", giving up", e);
                    proc.appDied();
                    stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
                            "2nd-crash", false);
                    return false;
                }

                // This is the first time we failed -- restart process and
                // retry.
                r.launchFailed = true;
                proc.removeActivity(r);
                throw e;
            }
        } finally {
            endDeferResume();
        }

        ...代码省略...

        return true;
    }

这里面的结构在Android28的时候做了比较大的变动,具体在Android9.0 Activity启动原理差异解析这篇文章有详细说到
新增了LaunchActivityItem,ResumeActivityItem, PauseActivityItem, StopActivityItem, DestroyActivityItem这五个类来实现Activity的五个生命周期。这几个类都是实现了ActivityLifecycleItem接口,有TransactionExecutor统一调度。 本文不做过多详解,这里直接总结下:

  1. 创建activity启动的事务,并且添加到clientTransaction对象的列表当中去,这个列表之后执行生命周期的时候会用到,需要记下。
  2. 设置最终期望到达的状态(我这里主要探讨的是activity启动时的事务,所以这里期望状态必然是resume状态)到事务当中去
  3. 执行事务(主要就是执行Activity的生命周期)

在创建事务的时候会将ActivityRecord的token传入,执行事务的时候会通过Binder机制调回到了客户端这边,然后执行LaunchActivityItem的时候execute方法的时候初始化ActivityClientRecord,并将Token传入到ActivityClientRecord中,而ActivityClientRecord便是客户端用于保存Activity数据的对象,与服务器进行数据交互主要就是通过Token来实现。


了解完客户端与服务端之间的数据通信之后,我们再来看下系统是系统是根据什么恢复现场的吧。这里就不得不提到两个方法:onSaveInstance,onRestoreInstance,一个是保存数据的方法,一个是加载数据的方法。但是有很多人都陷入了一个误区以为onSaveInstance是在Activity被回收的时候才会调用。其实不然,我们可以看下onSaveInstance和onRestoreInstance 的调用时机:

onSaveInstance

我们直接看代码了解下它是什么时候调用的:

    /**
     * The hook for {@link ActivityThread} to save the state of this activity.
     *
     * Calls {@link #onSaveInstanceState(android.os.Bundle)}
     * and {@link #saveManagedDialogs(android.os.Bundle)}.
     *
     * @param outState The bundle to save the state to.
     */
    final void performSaveInstanceState(@NonNull Bundle outState) {
        dispatchActivityPreSaveInstanceState(outState);
        onSaveInstanceState(outState);
        saveManagedDialogs(outState);
        mActivityTransitionState.saveState(outState);
        storeHasCurrentPermissionRequest(outState);
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
        dispatchActivityPostSaveInstanceState(outState);
    }

发现是在Activity的performSaveInstanceState中调用,我们继续找调用的目标:

public class Instrumentation {
    /**
     * Perform calling of an activity's {@link Activity#onSaveInstanceState}
     * method.  The default implementation simply calls through to that method.
     *  @param activity The activity being saved.
     * @param outState The bundle to pass to the call.
     * @param outPersistentState The persistent bundle to pass to the call.
     */
    public void callActivityOnSaveInstanceState(@NonNull Activity activity,
            @NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
        activity.performSaveInstanceState(outState, outPersistentState);
    }
}

是由Instrumentation 调用,看到这个类相信应该比较熟悉了。基本上ActivityThread要操作Activity都是通过这个类执行的,那么callActivityOnSaveInstanceState自然应该也是ActivityThread里面调用的,那么我们继续跟下去看下:

   /**
     * Calls {@link Activity#onStop()} and {@link Activity#onSaveInstanceState(Bundle)}, and updates
     * the client record's state.
     * All calls to stop an activity must be done through this method to make sure that
     * {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
     */
    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值只有在Activity要被销毁的时候才会设置为false,其他情况下都是true
        //如果Activity只是看不见了,那么r.activity.mFinished必然为false
        //而r.state每次在onResume的时候就会置为空, 所以onStop的时候r.state == null必然为true
        //r.isPreHoneycomb()指的是版本号要HONEYCOMB之后才会置true(版本在HONEYCOMB之前onSaveInstance是在onPause之前调用的)
        final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
                && !r.isPreHoneycomb();
        final boolean isPreP = r.isPreP();
        if (shouldSaveState && isPreP) {
            callActivityOnSaveInstanceState(r);
        }

        try {
            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);

        if (shouldSaveState && !isPreP) {
            callActivityOnSaveInstanceState(r);
        }
    }

    private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
        r.state = new Bundle();
        r.state.setAllowFds(false);
        if (r.isPersistable()) {
            r.persistentState = new PersistableBundle();
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                    r.persistentState);
        } else {
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    }

从这段代码可以看出onSaveInstanceState与onStop(r.activity.performStop最终会执行onStop方法)的执行顺序与android版本有关,在Android P 之前onSaveInstance是在onStop之前调用,在Android P 之后onSaveInstance就是在onStop之后调用。但是总的来说onSaveInstance就是在Activity已经"看不见"了以后就会执行,并非Activity被回收的时候才执行。而onStop方法其实是在StopActivityItem的execute方法里面执行的,然后调用handleStopActivity方法:

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

        final StopInfo stopInfo = new StopInfo();
        //callActivityOnStop方法就在这里面调用
        performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
                reason);

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

        updateVisibility(r, show);

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

上文提到的callActivityOnStop就在performStopActivityInner里面调用。
然后初始化一个StopInfo对象,这是一个PendingTransactionActions的内部类,实现了Runnable接口(后续会调用该对象的run方法的)。将saveInstance的state保存进去,然后存进了PendingTransactionActions对象中。这里的操作告一段落。然后我们回到TransactionExecutor里面看下调用的地方:

    /** Cycle through all states requested by callbacks and execute them at proper times. */
    @VisibleForTesting
    public void executeCallbacks(ClientTransaction transaction) {
        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
        ...代码省略...

        final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
            ...代码省略...

            item.execute(mTransactionHandler, token, mPendingActions);
            item.postExecute(mTransactionHandler, token, mPendingActions);
         

           ...代码省略...
        }
    }

这个方法的核心代码就是这两句,item.execute就会执行对应ActivityLifeCycleItem的execute方法。最后会执行Activity的生命周期.上文我们执行完stop方法之后,接下来应该就是执行postExecute方法了:

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

代码很少,会执行client的reportStop方法,client是ClientTransactionHandler对象,也就是我们的ActivityThread(继承了ClientTransactionHandler)

    @Override
    public void reportStop(PendingTransactionActions pendingActions) {
        mH.post(pendingActions.getStopInfo());
    }

mH是Handler对象,post的是一个Runnable对象,还记得刚才说的StopInfo么?没错就是现在post出去的那一个。我们继续看下StopInfo的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.
                ActivityTaskManager.getService().activityStopped(
                        mActivity.token, mState, mPersistentState, mDescription);
            } catch (RemoteException 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 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.rethrowFromSystemServer();
            }

这里会获取ATMS对象,执行activityStopped方法。那么ATMS又是什么?其实是Android29以后新增的一个管理Activity生命周期的服务端的类,就是从AMS里面抽取出来的。此处将token传了进入,我们也可以看到之前saveInstance之后的mState也传了进去。后面大致也能猜测到: 1.服务端通过Token找到对应的ActivityRecord对象. 2.更新ActivityRecord的Bundle对象。那么我们看下代码来验证下猜想吧:

    @Override
    public final void activityStopped(IBinder token, Bundle icicle,
            PersistableBundle persistentState, CharSequence description) {
        ...代码省略...
        final ActivityRecord r;
        synchronized (mGlobalLock) {
            r = ActivityRecord.isInStackLocked(token);
            if (r != null) {
                if (r.attachedToProcess()
                        && r.isState(ActivityStack.ActivityState.RESTARTING_PROCESS)) {
                    // The activity was requested to restart from
                    // {@link #restartActivityProcessIfVisible}.
                    restartingName = r.app.mName;
                    restartingUid = r.app.mUid;
                }
                r.activityStoppedLocked(icicle, persistentState, description);
            }
        }
       ...代码省略...
    }

r = ActivityRecord.isInStackLocked(token);这段代码印证了猜想1,确实是通过token找到对应的ActivityRecord。那么继续往下看activityStoppedLocked的处理逻辑:

    final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
            CharSequence description) {
        ...代码省略...

        if (newIcicle != null) {
            // If icicle is null, this is happening due to a timeout, so we haven't really saved
            // the state.
            icicle = newIcicle;
            haveState = true;
            launchCount = 0;
            updateTaskDescription(description);
        }
        ...代码省略...
    }

这里就印证了猜想2,将客户端的Bundle对象保存到了服务端ActivityRecord里面。这样保存现场的操作也就完成了。


保存现场数据.jpg

接下来就是如何恢复现场了:

onRestoreInstance

而Activity每次可见的时候onRestoreInstance并非每次都会调用,那么什么时候调用呢,继续往下看源码:

    /**
     * The hook for {@link ActivityThread} to restore the state of this activity.
     *
     * Calls {@link #onSaveInstanceState(android.os.Bundle)} and
     * {@link #restoreManagedDialogs(android.os.Bundle)}.
     *
     * @param savedInstanceState contains the saved state
     */
    final void performRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        onRestoreInstanceState(savedInstanceState);
        restoreManagedDialogs(savedInstanceState);
    }

调用逻辑应该是类似的,我们可以直接进入ActivityThread查看调用方:

    @Override
    public void handleStartActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions) {
        final Activity activity = r.activity;
        if (r.activity == null) {
            // TODO(lifecycler): What do we do in this case?
            return;
        }
        if (!r.stopped) {
            throw new IllegalStateException("Can't start activity that is not stopped.");
        }
        if (r.activity.mFinished) {
            // TODO(lifecycler): How can this happen?
            return;
        }

        // Start
        activity.performStart("handleStartActivity");
        r.setState(ON_START);

        if (pendingActions == null) {
            // No more work to do.
            return;
        }

        // Restore instance state
        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) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
            }
        }

        // Call postOnCreate()
        if (pendingActions.shouldCallOnPostCreate()) {
            activity.mCalled = false;
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnPostCreate(activity, r.state,
                        r.persistentState);
            } else {
                mInstrumentation.callActivityOnPostCreate(activity, r.state);
            }
            if (!activity.mCalled) {
                throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString()
                                + " did not call through to super.onPostCreate()");
            }
        }
    }

可以看到只有pendingActions.shouldRestoreInstanceState()为true的时候并且state不为null的时候才会调用callActivityOnRestoreInstanceState,而pendingActions.shouldRestoreInstanceState()为true的前提是Activity执行了onCreate的时候才会设置为true,而state只有在之前有过保存的时候才会不为null。因此onRestoreInstance只有在Activity新建的时候才会调用,而onSaveInstance是在Activity"看不见了"以后就会调用。所以这两个方法并不会成对调用。

那么什么情况下state不为null呢?

  1. 在Activity退回到后台时,执行了onSaveInstance,这个时候state对象就初始化,
  2. 将state传给服务端以后,当Activity被系统杀死时,服务端会保留对应的ActivityRecord对象,该对象保存着state数据。
  3. APP重启的时候,会对应的ActiviyRecord会将state传回给客户端,让客户端恢复现场。

题外话

  1. 背景
    很多同学都喜欢用静态变量保存全局公用的参数,这么做就可以不用再通过Intent一层一层传值,逻辑上面会简单不少。但是APP也会因为静态变量导致空指针crash。
  2. 原因
    为何?虽然静态变量拥有超长的生命周期,他可以跟整个进程共存亡,理论上来说静态变量是不会出现空指针的情况。但是一旦APP由于系统内存紧张被干掉以后,静态变量也就会存着被回收的可能,而此时一旦APP被重启以后必然需要恢复现场,这个时候就会出现静态变量空指针的异常。
  3. 解决方案
    那么怎么解决上面的问题呢?两种方案:1.去掉静态变量的使用,通过intent传值。因为intent跟内部的state一样都会在服务端保存一份,即便APP进程被杀掉以后也不会被回收掉。2.在onSaveInstance的时候将静态变量也保存起来,恢复现场的时候在给静态变量重新赋值。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,719评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,337评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,887评论 0 324
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,488评论 1 266
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,313评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,284评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,672评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,346评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,644评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,700评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,457评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,316评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,706评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,261评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,648评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,859评论 2 335

推荐阅读更多精彩内容