Analyze the Activity startup process


I haven’t posted my blog for almost two years. During this period, I have been engaged in the development of Android system, so I don’t have much time to write articles. At the same time, because of my lack of knowledge accumulation, I found that my knowledge was too poor after going into the bottom development, so I have been settling myself. Two years to learn a lot of the knowledge of the system framework layer, horizontal knowledge or longitudinal depth has improved, recently will take time I know during the period of the knowledge and the module with source code analysis again, on the one hand, through the analysis of their own impression, but also let the students little detours, source code analysis process is very boring, but it is also the embodiment of the basic skills, I will also try to write easy to understand some, at the same time if there are mistakes in the article, or not easy to understand the place, but also hope to help correct, we learn together, together raise salary.

Introduction to the

In the process of App development, we start an Activity operation, it is normal, but most of the time, we have not thought about the process behind, this chapter I will combine the source code, analyze the process, take a look, the system in the end for us to do what the source code in the article because of length reasons, may be omitted, The full source code can be viewed at, where I’m using pie-9.0.0_R3

Began to call

The entry function ContextWrapper startActivity

To start an Activity, we first call the Activity’s startActivity() method and click in to see what’s actually called ContextWrapper’s startActivity() method. Look at this method:

    public void startActivity(Intent intent) {
Copy the code

Instead of implementing it, we call the startActivity method of mBase, which is actually a ContextImpl instance passed in


// /frameworks/base/core/java/android/app/ @Override public void startActivity(Intent intent) { warnIfCallingFromSystemProcess(); startActivity(intent, null); } /** @hide */ @Override public void startActivityAsUser(Intent intent, UserHandle user) { startActivityAsUser(intent, null, user); } @Override public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is // generally not allowed, except if the caller specifies the task id the activity should // be launched in. A bug was existed between N and O-MR1 which allowed this to work. We // maintain this for backwards compatibility. final int targetSdkVersion = getApplicationInfo().targetSdkVersion; if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && (targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.P) && (options == null || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires  the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options); }Copy the code

The execStartActivity method is used to create the ActivityThread instance object in the Instrumentation method. Find the Java file and take a look at the implementation


// /frameworks/base/core/java/android/app/ public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread; Uri referrer = target ! = null ? target.onProvideReferrer() : null; if (referrer ! = null) { intent.putExtra(Intent.EXTRA_REFERRER, referrer); } if (mActivityMonitors ! = null) { synchronized (mSync) { final int N = mActivityMonitors.size(); for (int i=0; i<N; i++) { final ActivityMonitor am = mActivityMonitors.get(i); ActivityResult result = null; if (am.ignoreMatchingSpecificIntents()) { result = am.onStartActivity(intent); } if (result ! = null) { am.mHits++; return result; } else if (am.match(who, null, intent)) { am.mHits++; if (am.isBlocking()) { return requestCode >= 0 ? am.getResult() : null; } break; } } } } try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(who); int result = ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! = null ? target.mEmbeddedID : null, requestCode, 0, null, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } return null; }Copy the code

You can see that the startActivity method of ActivityManagerService is actually called through ActivityManager


Other features of ActivityManagerService will not be covered here, but will be described in a later article

    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,

    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
                true /*validateIncomingUser*/);

    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
            boolean validateIncomingUser) {

        userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        // TODO: Switch to user app stacks here.
        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")

Copy the code

See what have done here, the first is testing program is enforced, will throw a SecurityException then is to get to the caller’s user id is the last call ActivityStartController. ObtainStarter, set all the launch parameters, Returns an ActivityStarter object and executes the execute method


// /frameworks/base/services/core/java/com/android/server/am/ int execute() { try { // TODO(b/64750076): Look into passing request directly to these methods to allow // for transactional diffs and preprocessing. if (mRequest.mayWait) { return startActivityMayWait(mRequest.caller, mRequest.callingUid, mRequest.callingPackage, mRequest.intent, mRequest.resolvedType, mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, mRequest.resultWho, mRequest.requestCode, mRequest.startFlags, mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig, mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId, mRequest.inTask, mRequest.reason, mRequest.allowPendingRemoteAnimationRegistryLookup); } else { return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo, mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, mRequest.resultWho, mRequest.requestCode, mRequest.callingPid, mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid, mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.componentSpecified, mRequest.outActivity, mRequest.inTask, mRequest.reason, mRequest.allowPendingRemoteAnimationRegistryLookup); } } finally { onExecutionComplete(); }}Copy the code

There are two methods, and since the setMayWait(userId) method was called above, we’ll go to startActivityMayWait and look at the implementation of that method


    // /frameworks/base/services/core/java/com/android/server/am/
    private int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult,
            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
            int userId, TaskRecord inTask, String reason,
            boolean allowPendingRemoteAnimationRegistryLookup) {
        // Refuse possible leaked file descriptors
        if (intent != null && intent.hasFileDescriptors()) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        boolean componentSpecified = intent.getComponent() != null;
        final int realCallingPid = Binder.getCallingPid();
        final int realCallingUid = Binder.getCallingUid();

        int callingPid;
        if (callingUid >= 0) {
            callingPid = -1;
        } else if (caller == null) {
            callingPid = realCallingPid;
            callingUid = realCallingUid;
        } else {
            callingPid = callingUid = -1;

        // Save a copy in case ephemeral needs it
        final Intent ephemeralIntent = new Intent(intent);
        // Don't modify the client's object!
        intent = new Intent(intent);
        if (componentSpecified
                && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
                && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
                && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
                && mService.getPackageManagerInternalLocked()
                        .isInstantAppInstallerComponent(intent.getComponent())) {
            // intercept intents targeted directly to the ephemeral installer the
            // ephemeral installer should never be started with a raw Intent; instead
            // adjust the intent so it looks like a "normal" instant app launch
            intent.setComponent(null /*component*/);
            componentSpecified = false;

        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                0 /* matchFlags */,
                                callingUid, realCallingUid, mRequest.filterCallingUid));
        if (rInfo == null) {
            UserInfo userInfo = mSupervisor.getUserInfo(userId);
            if (userInfo != null && userInfo.isManagedProfile()) {
                // Special case for managed profiles, if attempting to launch non-cryto aware
                // app in a locked managed profile from an unlocked parent allow it to resolve
                // as user will be sent via confirm credentials to unlock the profile.
                UserManager userManager = UserManager.get(mService.mContext);
                boolean profileLockedAndParentUnlockingOrUnlocked = false;
                long token = Binder.clearCallingIdentity();
                try {
                    UserInfo parent = userManager.getProfileParent(userId);
                    profileLockedAndParentUnlockingOrUnlocked = (parent != null)
                            && userManager.isUserUnlockingOrUnlocked(
                            && !userManager.isUserUnlockingOrUnlocked(userId);
                } finally {
                if (profileLockedAndParentUnlockingOrUnlocked) {
                    rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                                    callingUid, realCallingUid, mRequest.filterCallingUid));
        // Collect information about the target of the Intent.
        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
        synchronized (mService) {
            final ActivityStack stack = mSupervisor.mFocusedStack;
            stack.mConfigWillChange = globalConfig != null
                    && mService.getGlobalConfiguration().diff(globalConfig) != 0;
                    "Starting activity when config will change = " + stack.mConfigWillChange);
            final long origId = Binder.clearCallingIdentity();
            //若ActivityInfo不为null,并且有PRIVATE_FLAG_CANT_SAVE_STATE标记,意味着调用则为heavy-weight process
            if (aInfo != null &&
                            & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 &&
                    mService.mHasHeavyWeightFeature) {
                // This may be a heavy-weight process!  Check to see if we already
                // have another, different heavy-weight process running.
                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
                    final ProcessRecord heavy = mService.mHeavyWeightProcess;
                    if (heavy != null && ( != aInfo.applicationInfo.uid
                            || !heavy.processName.equals(aInfo.processName))) {
                        int appCallingUid = callingUid;
                        if (caller != null) {
                            ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
                            if (callerApp != null) {
                                appCallingUid =;
                            } else {
                                Slog.w(TAG, "Unable to find app for caller " + caller
                                        + " (pid=" + callingPid + ") when starting: "
                                        + intent.toString());
                                return ActivityManager.START_PERMISSION_DENIED;

                        IIntentSender target = mService.getIntentSenderLocked(
                                ActivityManager.INTENT_SENDER_ACTIVITY, "android",
                                appCallingUid, userId, null, null, 0, new Intent[] { intent },
                                new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
                                        | PendingIntent.FLAG_ONE_SHOT, null);
                        Intent newIntent = new Intent();
                        if (requestCode >= 0) {
                            // Caller is requesting a result.
                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
                                new IntentSender(target));
                        if (heavy.activities.size() > 0) {
                            ActivityRecord hist = heavy.activities.get(0);
                        intent = newIntent;
                        resolvedType = null;
                        caller = null;
                        callingUid = Binder.getCallingUid();
                        callingPid = Binder.getCallingPid();
                        componentSpecified = true;
                        rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId,
                                0 /* matchFlags */, computeResolveFilterUid(
                                        callingUid, realCallingUid, mRequest.filterCallingUid));
                        aInfo = rInfo != null ? rInfo.activityInfo : null;
                        if (aInfo != null) {
                            aInfo = mService.getActivityInfoForUser(aInfo, userId);
            final ActivityRecord[] outRecord = new ActivityRecord[1];
            int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
                    ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,


            if (stack.mConfigWillChange) {
                // If the caller also wants to switch to a new configuration,
                // do so now.  This allows a clean switch, as we are waiting
                // for the current activity to pause (so we will not destroy
                // it), and have not yet started the next activity.
                stack.mConfigWillChange = false;
                        "Updating to new configuration after starting activity.");
                mService.updateConfigurationLocked(globalConfig, null, false);
            if (outResult != null) {
                outResult.result = res;

                final ActivityRecord r = outRecord[0];
                switch(res) {
                    case START_SUCCESS: {
                        do {
                            try {
                                //一直等待,直到outResult显示Activity对应的Task成为Front task
                            } catch (InterruptedException e) {
                        } while (outResult.result != START_TASK_TO_FRONT
                                && !outResult.timeout && outResult.who == null);
                        if (outResult.result == START_TASK_TO_FRONT) {
                            res = START_TASK_TO_FRONT;
                    case START_DELIVERED_TO_TOP: {
                        outResult.timeout = false;
                        outResult.who = r.realActivity;
                        outResult.totalTime = 0;
                        outResult.thisTime = 0;
                    case START_TASK_TO_FRONT: {
                        // ActivityRecord may represent a different activity, but it should not be
                        // in the resumed state.
                        if (r.nowVisible && r.isState(RESUMED)) {
                            outResult.timeout = false;
                            outResult.who = r.realActivity;
                            outResult.totalTime = 0;
                            outResult.thisTime = 0;
                        } else {
                            outResult.thisTime = SystemClock.uptimeMillis();
                            mSupervisor.waitActivityVisible(r.realActivity, outResult);
                            // Note: the timeout variable is not currently not ever set.
                            do {
                                try {
                                } catch (InterruptedException e) {
                            } while (!outResult.timeout && outResult.who == null);

            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);
            return res;
Copy the code

There is a lot of code in this, in order to understand, I add the comments in the code inside the summary: Parse the ActivityInfo corresponding to the Intent, get the foreground Task that started the Activity, start the Activity with startActivity, process the result, and then move on, call the startActivity method, This method calls the more important method startActivityUnchecked, check the source code

Start mode and other related processing


    // /frameworks/base/services/core/java/com/android/server/am/
    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {

        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,


        ActivityRecord reusedActivity = getReusableIntentActivity();

        int preferredWindowingMode = WINDOWING_MODE_UNDEFINED;
        int preferredLaunchDisplayId = DEFAULT_DISPLAY;
        if (mOptions != null) {
            preferredWindowingMode = mOptions.getLaunchWindowingMode();
            preferredLaunchDisplayId = mOptions.getLaunchDisplayId();

        // windowing mode and preferred launch display values from {@link LaunchParams} take
        // priority over those specified in {@link ActivityOptions}.
        if (!mLaunchParams.isEmpty()) {
            if (mLaunchParams.hasPreferredDisplay()) {
                preferredLaunchDisplayId = mLaunchParams.mPreferredDisplayId;

            if (mLaunchParams.hasWindowingMode()) {
                preferredWindowingMode = mLaunchParams.mWindowingMode;

        if (reusedActivity != null) {
            // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
            // still needs to be a lock task mode violation since the task gets cleared out and
            // the device would otherwise leave the locked task.
            if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
                    (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
                Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");

            // True if we are clearing top and resetting of a standard (default) launch mode
            // ({@code LAUNCH_MULTIPLE}) activity. The existing activity will be finished.
            final boolean clearTopAndResetStandardLaunchMode =
                    && mLaunchMode == LAUNCH_MULTIPLE;

            // If mStartActivity does not have a task associated with it, associate it with the
            // reused activity's task. Do not do so if we're clearing top and resetting for a
            // standard launchMode activity.
            if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {

            if (reusedActivity.getTask().intent == null) {
                // This task was started because of movement of the activity based on affinity...
                // Now that we are actually launching it, we can assign the base intent.

            // This code path leads to delivering a new intent, we want to make sure we schedule it
            // as the first operation, in case the activity will be resumed as a result of later
            // operations.
            if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                    || isDocumentLaunchesIntoExisting(mLaunchFlags)
                    || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
                final TaskRecord task = reusedActivity.getTask();

                // In this situation we want to remove all activities from the task up to the one
                // being started. In most cases this means we are resetting the task to its initial
                // state.
                final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,

                // The above code can remove {@code reusedActivity} from the task, leading to the
                // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
                // task reference is needed in the call below to
                // {@link setTargetStackAndMoveToFrontIfNeeded}.
                if (reusedActivity.getTask() == null) {

                if (top != null) {
                    if (top.frontOfTask) {
                        // Activity aliases may mean we use different intents for the top activity,
                        // so make sure the task now has the identity of the new intent.

            mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);

            reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);

            final ActivityRecord outResult =
                    outActivity != null && outActivity.length > 0 ? outActivity[0] : null;

            // When there is a reused activity and the current result is a trampoline activity,
            // set the reused activity as the result.
            if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
                outActivity[0] = reusedActivity;

            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                // We don't need to start a new activity, and the client said not to do anything
                // if that is the case, so this is it!  And for paranoia, make sure we have
                // correctly resumed the top activity.
                return START_RETURN_INTENT_TO_CALLER;

            if (reusedActivity != null) {

                if (!mAddingToTask && mReuseTask == null) {
                    // We didn't do anything...  but it was needed (a.k.a., client don't use that
                    // intent!)  And for paranoia, make sure we have correctly resumed the top activity.

                    if (outActivity != null && outActivity.length > 0) {
                        outActivity[0] = reusedActivity;

                    return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;

        if (mStartActivity.packageName == null) {
            final ActivityStack sourceStack = mStartActivity.resultTo != null
                    ? mStartActivity.resultTo.getStack() : null;
            if (sourceStack != null) {
                sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
                        mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
                        null /* data */);
            return START_CLASS_NOT_FOUND;

        // If the activity being launched is the same as the one currently at the top, then
        // we need to check if it should only be launched once.
        final ActivityStack topStack = mSupervisor.mFocusedStack;
        final ActivityRecord topFocused = topStack.getTopActivity();
        final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
        final boolean dontStart = top != null && mStartActivity.resultTo == null
                && top.realActivity.equals(mStartActivity.realActivity)
                && top.userId == mStartActivity.userId
                && != null && != null
                && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
        if (dontStart) {
            // For paranoia, make sure we have correctly resumed the top activity.
            topStack.mLastPausedActivity = null;
            if (mDoResume) {
            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                // We don't need to start a new activity, and the client said not to do
                // anything if that is the case, so this is it!
                return START_RETURN_INTENT_TO_CALLER;


            // Don't use mStartActivity.task to show the toast. We're not starting a new activity
            // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
            mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
                    preferredLaunchDisplayId, topStack);

            return START_DELIVERED_TO_TOP;

        boolean newTask = false;
        final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                ? mSourceRecord.getTask() : null;

        // Should this be considered a new task?
        int result = START_SUCCESS;
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            newTask = true;
            result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);
        } else if (mSourceRecord != null) {
            result = setTaskFromSourceRecord();
        } else if (mInTask != null) {
            result = setTaskFromInTask();
        } else {
            // This not being started from an existing activity, and not part of a new task...
            // just put it in the top task, though these days this case should never happen.
        if (result != START_SUCCESS) {
            return result;

        mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
                mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
        mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
                mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
        if (newTask) {
            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
        mTargetStack.mLastPausedActivity = null;

        mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
        mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
        if (mDoResume) {
            final ActivityRecord topTaskActivity =
            if (!mTargetStack.isFocusable()
                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay
                    && mStartActivity != topTaskActivity)) {
                // If the activity is not focusable, we can't resume it, but still would like to
                // make sure it becomes visible as it starts (this will also trigger entry
                // animation). An example of this are PIP activities.
                // Also, we don't want to resume activities in a task that currently has an overlay
                // as the starting activity just needs to be in the visible paused state until the
                // over is removed.
                mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                // Go ahead and tell window manager to execute app transition for this activity
                // since the app transition will not be triggered through the resume channel.
            } else {
                // If the target stack was not previously focusable (previous top running activity
                // on that stack was not visible) then any prior calls to move the stack to the
                // will not update the focused stack.  If starting the new activity now allows the
                // task stack to be focusable, then ensure that we now update the focused stack
                // accordingly.
                if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
                mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
        } else if (mStartActivity != null) {
        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);

        mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
                preferredLaunchDisplayId, mTargetStack);

        return START_SUCCESS;
Copy the code

The whole process here is mainly the processing of the Activity startup mode 1. Calculate the DLAG of the startup 2. There are also special activity patterns and flag handling for finding tasks, as well as reuse of activities, including the following: LaunchSingleInstance LaunchSingleTask SingleTop && Activity is already at the top of the stack. If you specify the FLAG_ACTIVITY_NEW_TASK flag, Or the Activity mode to start is SingleInstance and SingleTask, or moption.getLaunchTasKid () specifies tasKid 3. Start a new task (FLAG_ACTIVITY_NEW_TASK is specified, FLAG_ACTIVITY_NEW_TASK is not specified in SourceRecord (the Activity that calls startActivity). And SourceRecord is not Null and starts in the specified task, and half is used to restore the task to start in the task in the current focus stack (neither FLAG_ACTIVITY_NEW_TASK nor SourceRecord) 4. Set up the corresponding task and brought to the front desk, to start the corresponding Activity (mTargetStack. StartActivityLocked). 5. Call mSupervisor. ResumeFocusedStackTopActivityLocked make visible Activity

Then look at the startActivityLocked method that starts the Activity, which is in ActivityStack, and see what happens

The loading of tasks and activities


    // /frameworks/base/services/core/java/com/android/server/am/
    void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
            boolean newTask, boolean keepCurTransition, ActivityOptions options) {
        TaskRecord rTask = r.getTask();
        final int taskId = rTask.taskId;
        // mLaunchTaskBehind tasks get placed at the back of the task stack.
        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
            // Last activity in task had been removed or ActivityManagerService is reusing task.
            // Insert or replace.
            // Might not even be in.
            insertTaskAtTop(rTask, r);
        TaskRecord task = null;
        if (!newTask) {
            // If starting in an existing task, find where that is...
            boolean startIt = true;
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                task = mTaskHistory.get(taskNdx);
                if (task.getTopActivity() == null) {
                    // All activities in task are finishing.
                if (task == rTask) {
                    // Here it is!  Now, if this is not yet visible to the
                    // user, then just add it without starting; it will
                    // get started when the user navigates back to it.
                    if (!startIt) {
                        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
                                + task, new RuntimeException("here").fillInStackTrace());
                } else if (task.numFullscreen > 0) {
                    startIt = false;

        // Place a new activity at top of stack, so it is next to interact with the user.

        // If we are not placing the new activity frontmost, we do not want to deliver the
        // onUserLeaving callback to the actual frontmost activity
        final TaskRecord activityTask = r.getTask();
        if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
            mStackSupervisor.mUserLeaving = false;
                    "startActivity() behind front, mUserLeaving=false");

        task = activityTask;

        // Slot the activity into the history stack and proceed
        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
                new RuntimeException("here").fillInStackTrace());
        // TODO: Need to investigate if it is okay for the controller to already be created by the
        // time we get to this point. I think it is, but need to double check.
        // Use test in b/34179495 to trace the call path.

        if (r.getWindowContainerController() == null) {

        if (!isHomeOrRecentsStack() || numActivities() > 0) {
                    "Prepare open transition: starting " + r);
            if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
            } else {
                int transit = TRANSIT_ACTIVITY_OPEN;
                if (newTask) {
                    if (r.mLaunchTaskBehind) {
                        transit = TRANSIT_TASK_OPEN_BEHIND;
                    } else {
                        // If a new task is being launched, then mark the existing top activity as
                        // supporting picture-in-picture while pausing only if the starting activity
                        // would not be considered an overlay on top of the current activity
                        // (eg. not fullscreen, or the assistant)
                        if (canEnterPipOnTaskSwitch(focusedTopActivity,
                                null /* toFrontTask */, r, options)) {
                            focusedTopActivity.supportsEnterPipOnTaskSwitch = true;
                        transit = TRANSIT_TASK_OPEN;
                mWindowManager.prepareAppTransition(transit, keepCurTransition);
            boolean doShow = true;
            if (newTask) {
                // Even though this activity is starting fresh, we still need
                // to reset it to make sure we apply affinities to move any
                // existing activities from other tasks in to it.
                // If the caller has requested that the target task be
                // reset, then do so.
                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                    resetTaskIfNeededLocked(r, r);
                    doShow = topRunningNonDelayedActivityLocked(null) == r;
            } else if (options != null && options.getAnimationType()
                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
                doShow = false;
            if (r.mLaunchTaskBehind) {
                // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
                // tell WindowManager that r is visible even though it is at the back of the stack.
                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                // Figure out if we are transitioning from another activity that is
                // "has the same starting icon" as the next one.  This allows the
                // window manager to keep the previous window it had previously
                // created, if it still had one.
                TaskRecord prevTask = r.getTask();
                ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked();
                if (prev != null) {
                    // We don't want to reuse the previous starting preview if:
                    // (1) The current activity is in a different task.
                    if (prev.getTask() != prevTask) {
                        prev = null;
                    // (2) The current activity is already displayed.
                    else if (prev.nowVisible) {
                        prev = null;
                //isTaskSwitch 方法判断(r, focusedTopActivity)是否在同一个task
                r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
        } else {
            // If this is the first activity, don't do any fancy animations,
            // because there is nothing for it to animate on top of.
Copy the code

The main function of this method is to stack tasks and activities, create a Task for a newly started Activity, and save the Task in ActivityRecord

  1. Check whether it is a newly created task based on newTask. Otherwise, the task is retrieved from the history stack
  2. Set the stack to the foreground stack
  3. Determine if animation is required and prepare the application for conversion
  4. Setting up the startup window

Then come back read mSupervisor resumeFocusedStackTopActivityLocked


// /frameworks/base/services/core/java/com/android/server/am/ boolean resumeFocusedStackTopActivityLocked( ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) { if (! readyToResume()) { return false; } // Check if the activity (targetStack!) is not null and is in focus. If (targetStack! = null && isFocusedStack(targetStack)) { return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); } final ActivityRecord r = mFocusedStack.topRunningActivityLocked(); if (r == null || ! r.isState(RESUMED)) { mFocusedStack.resumeTopActivityUncheckedLocked(null, null); } else if (r.isState(RESUMED)) { // Kick off any lingering app transitions form the MoveTaskToFront operation. mFocusedStack.executeAppTransition(targetOptions); } return false; }Copy the code

This method is mainly to check the focus of the Activity stack, then call targetStack. ResumeTopActivityUncheckedLocked continue processing


// /frameworks/base/services/core/java/com/android/server/am/ @GuardedBy("mService") boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { if (mStackSupervisor.inResumeTopActivity) { // Don't even start recursing. return false; } boolean result = false; try { // Protect against recursion. mStackSupervisor.inResumeTopActivity = true; / / the Activity start the core method of result = resumeTopActivityInnerLocked (prev, options); // When resuming the top activity, it may be necessary to pause the top activity (for // example, returning to the lock screen. We suppress the normal pause logic in // {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the // end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here // to ensure any necessary pause logic occurs. In the case where the Activity will be // shown regardless of the lock screen, the call to // {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped. final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */); if (next == null || ! next.canTurnScreenOn()) { checkReadyForSleep(); } } finally { mStackSupervisor.inResumeTopActivity = false; } return result; }Copy the code

To call this place resumeTopActivityInnerLocked method, here is divided into several parts, the first is to suspend the current running Activity

Pause operation

ActivityStack. ResumeTopActivityInnerLocked (upper part Pause the Activity entry)

    // /frameworks/base/services/core/java/com/android/server/am/
    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        if (!mService.mBooting && !mService.mBooted) {
            // Not ready yet!
            return false;

        // 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.
        final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);

        final boolean hasRunningActivity = next != null;

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


        // Remember how we'll process this pause/resume situation, and ensure
        // that the state is reset however we wind up proceeding.
        boolean userLeaving = mStackSupervisor.mUserLeaving;
        mStackSupervisor.mUserLeaving = false;

        if (!hasRunningActivity) {
            // There are no activities left in the stack, let's look somewhere else.
            return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");

        next.delayedResume = false;

        // If the top activity is the resumed one, nothing to do.
        if (mResumedActivity == next && next.isState(RESUMED)
                && mStackSupervisor.allResumedActivitiesComplete()) {
            // Make sure we have executed any pending transitions, since there
            // should be nothing left to do at this point.
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Top activity resumed " + next);
            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
            return false;

        // If we are sleeping, and there is no resumed activity, and the top
        // activity is paused, well that is the state we want.
        //如果activity sleep或则已暂停,则是我们需要的状态,不需要再处理
        if (shouldSleepOrShutDownActivities()
                && mLastPausedActivity == next
                && mStackSupervisor.allPausedActivitiesComplete()) {
            // Make sure we have executed any pending transitions, since there
            // should be nothing left to do at this point.
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Going to sleep and all paused");
            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
            return false;

        // Make sure that the user who owns this activity is started.  If not,
        // we will just leave it as is because someone should be bringing
        // another user's activities to the top of the stack.
        if (!mService.mUserController.hasStartedUserState(next.userId)) {
            Slog.w(TAG, "Skipping resume of top activity " + next
                    + ": user " + next.userId + " is stopped");
            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
            return false;

        // The activity may be waiting for stop, but that is no longer
        // appropriate for it.
        next.sleeping = false;

        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);

        // If we are currently pausing an activity, then don't do anything until that is done.
        if (!mStackSupervisor.allPausedActivitiesComplete()) {
                    "resumeTopActivityLocked: Skip resume: some activity pausing.");
            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
            return false;

        //设置wake lock关联的Source,用于应用耗电统计

        boolean lastResumedCanPip = false;
        ActivityRecord lastResumed = null;
        final ActivityStack lastFocusedStack = mStackSupervisor.getLastStack();
        if (lastFocusedStack != null && lastFocusedStack != this) {
            // So, why aren't we using prev here??? See the param comment on the method. prev doesn't
            // represent the last resumed activity. However, the last focus stack does if it isn't null.
            lastResumed = lastFocusedStack.mResumedActivity;
            if (userLeaving && inMultiWindowMode() && lastFocusedStack.shouldBeVisible(next)) {
                // The user isn't leaving if this stack is the multi-window mode and the last
                // focused stack should still be visible.
                if(DEBUG_USER_LEAVING) Slog.i(TAG_USER_LEAVING, "Overriding userLeaving to false"
                        + " next=" + next + " lastResumed=" + lastResumed);
                userLeaving = false;
            lastResumedCanPip = lastResumed != null && lastResumed.checkEnterPictureInPictureState(
                    "resumeTopActivity", userLeaving /* beforeStopping */);
        // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity
        // to be paused, while at the same time resuming the new resume activity only if the
        // previous activity can't go into Pip since we want to give Pip activities a chance to
        // enter Pip before resuming the next activity.
        final boolean resumeWhilePausing = ( & FLAG_RESUME_WHILE_PAUSING) != 0
                && !lastResumedCanPip;
        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
        if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false, next, false);
Copy the code


StartPausingLocked will pause an Activity that is currently running. Let’s look at this method

// /frameworks/base/services/core/java/com/android/server/am/ final boolean startPausingLocked(boolean  userLeaving, boolean uiSleeping, ActivityRecord resuming, Boolean pauseImmediately) {//mPausingActivity specifies the activity currently being paused. If (mPausingActivity! = null) {, "Going to pause when pause is already pending for " + mPausingActivity + " state=" + mPausingActivity.getState()); if (! shouldSleepActivities()) { // Avoid recursion among check for sleep and complete pause during sleeping. // Because activity will be paused immediately after resume, just let pause // be completed by the order of activity paused from clients. completePauseLocked(false, resuming); }} // Check prev ActivityRecord prev = mResumedActivity; if (prev == null) { if (resuming == null) {, "Trying to pause when nothing is resumed"); mStackSupervisor.resumeFocusedStackTopActivityLocked(); } return false; } if (prev == resuming) {, "Trying to pause activity that is in process of being resumed"); return false; } // Assign prev to mPausingActivity, If (DEBUG_STATES) slog. v(TAG_STATES, "Moving to PAUSING: "+ prev); else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev); mPausingActivity = prev; mLastPausedActivity = prev; mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) ! = 0 || ( & ActivityInfo.FLAG_NO_HISTORY) ! = 0? prev : null; // Set the prev state to PAUSING, which sets mResumedActivity to null prev.setState(PAUSING, "startPausingLocked"); // Update the last active time of prev's task prev.gettask ().touchActiveTime(); // Set timeout to clearLaunchTime(prev); mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode()); mService.updateCpuStats(); If (prev. App! = null && ! = null) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev); try { EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev), prev.shortComponentName, "userLeaving=" + userLeaving); mService.updateUsageStats(prev, false); / / notify the target application process corresponding to the life of the Activity scheduling mService. GetLifecycleManager () scheduleTransaction (prev. App. Thread, prev. AppToken, PauseActivityItem.obtain(prev.finishing, userLeaving, prev.configChangeFlags, pauseImmediately)); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. Slog.w(TAG, "Exception thrown during pause", e); mPausingActivity = null; mLastPausedActivity = null; mLastNoHistoryActivity = null; } } else { mPausingActivity = null; mLastPausedActivity = null; mLastNoHistoryActivity = null; } // If we are not going to sleep, we want to ensure the device is // awake until the next activity is started. if (! uiSleeping && ! mService.isSleepingOrShuttingDownLocked()) { mStackSupervisor.acquireLaunchWakelock(); } // Normally mPausingActivity holds a reference in if (mPausingActivity! = null) { // Have the window manager pause its key dispatching until the new // activity has started. If we're pausing the activity just because // the screen is being turned off and the UI is sleeping, don't interrupt // key dispatch; the same activity will pick it up again on wakeup. if (! UiSleeping) {/ / pause event distribution prev. PauseKeyDispatchingLocked (); } else if (DEBUG_PAUSE) { Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off"); } if (pauseImmediately) { // If the caller said they don't want to wait for the pause, then complete // the pause now. completePauseLocked(false, resuming); return false; } else { schedulePauseTimeout(prev); return true; } } else { // This activity failed to schedule the // pause, so just treat it as being paused now. if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next."); / / resuming the default holds the next to display activityRecord if (resuming = = null) {mStackSupervisor. ResumeFocusedStackTopActivityLocked (); } return false; }}Copy the code

This method assigns mResumedActivity to mPausingActivity and sets the state to PAUSING, The mResumedActivity is then set to null and the corresponding App is notified by IApplicationThread to perform the pause operation. The scheduleTransaction method continues


// /frameworks/base/services/core/java/com/android/server/am/ void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken, @nonnull ActivityLifecycleItem stateRequest) throws RemoteException { Save the client, token, final stateRequest parameters ClientTransaction ClientTransaction = transactionWithState (client, activityToken. stateRequest); scheduleTransaction(clientTransaction); }Copy the code

ClientTransaction transaction object that encapsulates commands and parameters and sends them to the APP process for processing. In ClientTransaction, the scheduleTransaction method of IApplicationThread is called. Moving on to scheduleTransaction,AMS is called to the APP process via IApplicationThread. ScheduleTransaction triggers ApplicationThread scheduleTransaction method, This method also calls the scheduleTransaction method of ActivityThread’s parent class ClientTransactionHandler


    // /frameworks/base/core/java/android/app/
    void scheduleTransaction(ClientTransaction transaction) {
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
Copy the code

The abstract method sendMessage is implemented in ActivityThread


// /frameworks/base/core/java/android/app/ private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { if (DEBUG_MESSAGES) Slog.v( TAG, "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj); Message msg = Message.obtain(); msg.what = what; msg.obj = obj; msg.arg1 = arg1; msg.arg2 = arg2; if (async) { msg.setAsynchronous(true); } mH.sendMessage(msg); } to omit... case EXECUTE_TRANSACTION: final ClientTransaction transaction = (ClientTransaction) msg.obj; mTransactionExecutor.execute(transaction); if (isSystem()) { // Client transactions inside system process are recycled on the client side // instead of ClientLifecycleManager to avoid being cleared before this // message is handled. transaction.recycle(); } // TODO(lifecycler): Recycle locally scheduled transactions. break;Copy the code

In this approach, mH is used to switch from the binder thread to the main thread to handle the transaction. ClientTransaction transaction messages are executed through TransactionExecutor’s execute method in the corresponding case EXECUTE_TRANSACTION in mH’s handleMessage method.


// /frameworks/base/core/java/android/app/servertransaction/ public void execute(ClientTransaction transaction) { final IBinder token = transaction.getActivityToken(); log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token); // Iterate over the mActivityCallbacks in ClientTransaction executeCallbacks(Transaction); // Call the mLidecycleStateRequest member in ClientTransaction, which is the PauseActivityItem executeLifecycleState(Transaction) set in Ams; mPendingActions.clear(); log("End resolving transaction"); } @VisibleForTesting public void executeCallbacks(ClientTransaction transaction) { final List<ClientTransactionItem> callbacks = transaction.getCallbacks(); if (callbacks == null) { // No callbacks to execute, return early. return; } log("Resolving callbacks"); final IBinder token = transaction.getActivityToken(); ActivityClientRecord r = mTransactionHandler.getActivityClient(token); // In case when post-execution state of the last callback matches the final state requested // for the activity in this transaction, we won't do the last transition here and do it when // moving to final state instead (because it may contain additional parameters from server). final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest(); final int finalState = finalStateRequest ! = null ? finalStateRequest.getTargetState() : UNDEFINED; // Index of the last callback that requests some post-execution state. final int lastCallbackRequestingState = lastCallbackRequestingState(transaction); final int size = callbacks.size(); for (int i = 0; i < size; ++i) { final ClientTransactionItem item = callbacks.get(i); log("Resolving callback: " + item); final int postExecutionState = item.getPostExecutionState(); final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r, item.getPostExecutionState()); if (closestPreExecutionState ! = UNDEFINED) { cycleToPath(r, closestPreExecutionState); } item.execute(mTransactionHandler, token, mPendingActions); item.postExecute(mTransactionHandler, token, mPendingActions); if (r == null) { // Launch activity request will create an activity record. r = mTransactionHandler.getActivityClient(token); } if (postExecutionState ! = UNDEFINED && r ! = null) { // Skip the very last transition and perform it by explicit state request instead. final boolean shouldExcludeLastTransition = i == lastCallbackRequestingState && finalState == postExecutionState; cycleToPath(r, postExecutionState, shouldExcludeLastTransition); } } } /** Transition to the final state if requested by the transaction. */ private void executeLifecycleState(ClientTransaction transaction) { final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest(); if (lifecycleItem == null) { // No lifecycle request, return early. return; } log("Resolving lifecycle state: " + lifecycleItem); final IBinder token = transaction.getActivityToken(); final ActivityClientRecord r = mTransactionHandler.getActivityClient(token); if (r == null) { // Ignore requests for non-existent client records for now. return; } // Cycle to the state right before the final requested state. // This method will complement and execute the corresponding lifecycle phases of the method in the lifecycle order cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */); // Execute the final transition with proper parameters. lifecycleItem.execute(mTransactionHandler, token, mPendingActions); lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions); }Copy the code

As with ExecutelifeccleState, the Execute and postExecute methods of PauseActivityItem are called. Will call the execute ClientTransactionHandler handlePauseActivity method, PauseActivityItem. Execute

    // /frameworks/base/core/java/android/app/servertransaction/
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
        client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
Copy the code

HandlePauseActivity is implemented by ActivityThread


// /frameworks/base/core/java/android/app/ @Override public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, PendingTransactionActions pendingActions, String reason) { ActivityClientRecord r = mActivities.get(token); if (r ! = null) { if (userLeaving) { performUserLeavingActivity(r); } r.activity.mConfigChangeFlags |= configChanges; // Execute pause PauseActivity performPauseActivity(r, finished, Reason, pendingActions); // Make sure any pending writes are now committed. if (r.isPreHoneycomb()) { QueuedWork.waitToFinish(); } mSomeActivitiesChanged = true; } } private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason, PendingTransactionActions pendingActions) { if (r.paused) { if (r.activity.mFinished) { // If we are finishing, we won't call onResume() in certain cases. // So here we likewise don't want to call onPause() if the activity // isn't resumed. return null; } RuntimeException e = new RuntimeException( "Performing pause of activity that is not resumed: " + r.intent.getComponent().toShortString()); Slog.e(TAG, e.getMessage(), e); } if (finished) { r.activity.mFinished = true; } // Pre-Honeycomb apps always save their state before pausing final boolean shouldSaveState = ! r.activity.mFinished && r.isPreHoneycomb(); if (shouldSaveState) { callActivityOnSaveInstanceState(r); } performPauseActivityIfNeeded(r, reason); // Notify any outstanding on paused listeners ArrayList<OnActivityPausedListener> listeners; synchronized (mOnPauseListeners) { listeners = mOnPauseListeners.remove(r.activity); } int size = (listeners ! = null ? listeners.size() : 0); for (int i = 0; i < size; i++) { listeners.get(i).onPaused(r.activity); } final Bundle oldState = pendingActions ! = null ? pendingActions.getOldState() : null; if (oldState ! = null) { // We need to keep around the original state, in case we need to be created again. // But we only do this for pre-Honeycomb apps, which always save their state when // pausing, so we can not have them save their state when restarting from a paused // state. For HC and later, we want to (and can) let the state be saved as the // normal part of stopping the activity. if (r.isPreHoneycomb()) { r.state = oldState; } } return shouldSaveState ? r.state : null; } private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) { if (r.paused) { // You are already paused silly... return; } try { r.activity.mCalled = false; mInstrumentation.callActivityOnPause(r.activity); if (! r.activity.mCalled) { throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent) + " did not call through to super.onPause()"); } } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (! mInstrumentation.onException(r.activity, e)) { throw new RuntimeException("Unable to pause activity " + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } r.setState(ON_PAUSE); }Copy the code

The state saved in ActivityClientRecord is checked in the performPauseActivity method and onSaveInstanceState() is called as appropriate, Then call performPauseActivityIfNeeded method, in this method by Instrumentation eventually triggered the Activity’s onPause lifecycle callback methods.

When this is done, the postExecute method of the PauseActivityItem is then executed


// /frameworks/base/core/java/android/app/servertransaction/ @Override public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { if (mDontReport) { return; } try { // TODO(lifecycler): Use interface callback instead of AMS. ActivityManager.getService().activityPaused(token); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }}Copy the code

Then call AMS’s activityPaused to notify the Activity Pause that it has finished


// /frameworks/base/services/core/java/com/android/server/am/ @Override public final void activityPaused(IBinder token) { final long origId = Binder.clearCallingIdentity(); synchronized(this) { ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack ! = null) { stack.activityPausedLocked(token, false); } } Binder.restoreCallingIdentity(origId); }Copy the code

This calls activityPausedLocked of ActivityStack


// /frameworks/base/services/core/java/com/android/server/am/ final void activityPausedLocked(IBinder token, boolean timeout) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity paused: token=" + token + ", timeout=" + timeout); final ActivityRecord r = isInStackLocked(token); if (r ! = null) { mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); if (mPausingActivity == r) { if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r + (timeout ? " (due to timeout)" : " (pause complete)")); mService.mWindowManager.deferSurfaceLayout(); try { completePauseLocked(true /* resumeNext */, null /* resumingActivity */); } finally { mService.mWindowManager.continueSurfaceLayout(); } return; } else { EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, r.userId, System.identityHashCode(r), r.shortComponentName, mPausingActivity ! = null ? mPausingActivity.shortComponentName : "(none)"); if (r.isState(PAUSING)) { r.setState(PAUSED, "activityPausedLocked"); if (r.finishing) { if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of failed to pause activity: " + r); finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false, "activityPausedLocked"); } } } } mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, ! PRESERVE_WINDOWS); }Copy the code

This is going to call the completePauseLocked method again pletePauseLocked (Pasue Activity complete)

// /frameworks/base/services/core/java/com/android/server/am/ private void completePauseLocked(boolean ResumeNext, ActivityRecord) {assign PauseActivity to prev ActivityRecord prev = mPausingActivity; if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev); if (prev ! = null) { prev.setWillCloseOrEnterPip(false); final boolean wasStopping = prev.isState(STOPPING); prev.setState(PAUSED, "completePausedLocked"); If (prev. Finishing) {// Finish Activity if (DEBUG_PAUSE) slog. v(TAG_PAUSE, "Executing of Activity: "+ prev); prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false, "completedPausedLocked"); } else if ( ! = null) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev + " wasStopping=" + wasStopping + " visible=" + prev.visible); if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) { if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause, no longer waiting: " + prev); } if (prev.deferRelaunchUntilPaused) { // Complete the deferred relaunch that was waiting for pause to complete. if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev); prev.relaunchActivityLocked(false /* andResume */, prev.preserveWindowOnDeferredRelaunch); } else if (wasStopping) { // We are also stopping, the stop request must have gone soon after the pause. // We can't clobber it, because the stop confirmation will not be handled. // We don't need to schedule another stop, we only need to let it happen. prev.setState(STOPPING, "completePausedLocked"); } else if (! prev.visible || shouldSleepOrShutDownActivities()) { // Clear out any deferred client hide we might currently have. prev.setDeferHidingClient(false); // If we were visible then resumeTopActivities will release resources before // stopping addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */); } } else { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev); prev = null; } // It is possible the activity was freezing the screen before it was paused. // In that case go ahead and remove the freeze this activity has on the screen // since it is no longer visible. if (prev ! = null) {/ / stop screen freezes prev stopFreezingScreenLocked force (true / * * /); } mPausingActivity = null; } / / said that need to be an Activity scheduling resume if (resumeNext) {final ActivityStack topStack = mStackSupervisor. GetFocusedStack (); if (! topStack.shouldSleepOrShutDownActivities()) { mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null); } else { checkReadyForSleep(); ActivityRecord top = topStack.topRunningActivityLocked(); if (top == null || (prev ! = null && top ! = prev)) { // If there are no more activities available to run, do resume anyway to start // something. Also if the top activity on the stack is not the just paused // activity, we need to go ahead and resume it to ensure we complete an // in-flight app switch. mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } } if (prev ! = null) { prev.resumeKeyDispatchingLocked(); if ( ! = null && prev.cpuTimeAtResume > 0 && mService.mBatteryStatsService.isOnBattery()) { long diff = mService.mProcessCpuTracker.getCpuTimeForPid( - prev.cpuTimeAtResume; if (diff > 0) { BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics(); synchronized (bsi) { BatteryStatsImpl.Uid.Proc ps = bsi.getProcessStatsLocked(,; if (ps ! = null) { ps.addForegroundTimeLocked(diff); } } } } prev.cpuTimeAtResume = 0; // reset it } // Notify when the task stack has changed, but only if visibilities changed (not just // focus). Also if there is an active pinned stack - we always want to notify  it about // task stack changes, because its positioning may depend on it. if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause || getDisplay().hasPinnedStack()) { mService.mTaskChangeNotificationController.notifyTaskStackChanged(); mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false; } mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, ! PRESERVE_WINDOWS); }Copy the code

The completePauseLocked method stores the pause ActivityRecord to the mStoppingActivities member (ArrayList) of the ActivityStackSupervisor. It is used to stop and finish it later. MPausingActivity is then assigned to null, indicating that no Activity is currently being paused. And then again call resumeFocusedStackTopActivityLocked method, again to resume Activity.

It is divided into two parts due to length. Click to continue the Activity startup process (below)