In the process of App development, the jump between interfaces is very frequent, and the startActivity method is generally used to start another Activity in one Activity. How an Activity works in the Framework has been one of my puzzles for years. The following code analysis is based on Android 6.0.1. Since I own a Nexus 5, the latest code for this clunker is only 6.0.1. The following code is taken from a Demo App that starts another Activity.

startActivity(Intent(this, DemoActivity::class.java))
Copy the code

The startActivity method takes an Intent as its parameter, and it’s easy to see that the startActivity method in App actually calls the startActivity method in activity.java.

frameworks/base/core/java/android/app/Activity.java

    @Override
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }
Copy the code

The method startActivity of the same name is then called, except that the second argument is null.

frameworks/base/core/java/android/app/Activity.java

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if(options ! =null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1); }}Copy the code

We then continue to call the startActivityForResult method, and since we’re not passing the additional arguments that the Bundle carries, we end up with the else branch.

frameworks/base/core/java/android/app/Activity.java

    public void startActivityForResult(Intent intent, int requestCode) {
        startActivityForResult(intent, requestCode, null);
    }
Copy the code

The startActivityForResult method calls an overloaded version with three parameters: Intent, request code, and Bundle. The arguments passed to the following functions are the Intent instance passed from above, -1, and NULL. The function of this method is familiar to App development. It starts an Activity that expects a result when it finishes, and when the Activity exits, the onActivityResult() method is called with the given requestCode.

frameworks/base/core/java/android/app/Activity.java

    public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if(ar ! =null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
        } else {
            if(options ! =null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                mParent.startActivityFromChild(this, intent, requestCode); }}}Copy the code

MMainThread. GetApplicationThread () to obtain a ApplicationThread mToken IBinder type, execStartActivity method is defined in the Instrumentation, Move to Instrumentation immediately.

frameworks/base/core/java/android/app/Instrumentation.java

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread; .try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess();
            intresult = ActivityManagerNative.getDefault() .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

Call again in the execStartActivity function ActivityManagerNative. GetDefault () startActivity (…). , must first clear ActivityManagerNative getDefault () returns?

frameworks/base/core/java/android/app/ActivityManagerNative.java

    /** * Retrieves the system default or global Activity Manager */
    static public IActivityManager getDefault(a) {
        return gDefault.get();
    }
Copy the code

GDefault is a global variable that is a singleton object of type IActivityManager. We see the create() method getService(” activity “) from the ServiceManager, which is required to communicate with Binder, returns the remote interface to ActivityManagerService, ActivityManagerProxy namely.

frameworks/base/core/java/android/app/ActivityManagerNative.java

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create(a) {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager"."default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager"."default service = " + am);
            }
            returnam; }};Copy the code

CallingPackage —- Name of the current Activity package

ResolvedType —- Returns the MIME type of the intent

frameworks/base/core/java/android/app/ActivityManagerNative.java

class ActivityManagerProxy implements IActivityManager {...public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller ! =null ? caller.asBinder() : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        if(profilerInfo ! =null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if(options ! =null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        returnresult; }... }Copy the code

The NEXT call to AMS’s startActivity method is through Binder communication.

ProfilerInfo —- is used to pass the system private API for analyzer Settings.

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
            resultWho, requestCode, startFlags, profilerInfo, options,
            UserHandle.getCallingUserId());
    }
Copy the code

StartActivityAsUser is actually called inside the startActivity function.

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    @Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
        enforceNotIsolatedCaller("startActivity");
        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
                false, ALLOW_FULL_ONLY, "startActivity".null);
        // TODO:Here switch to the user App Stacks
        return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null.null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null.null, options, false, userId, null.null);
    }
Copy the code

MStackSupervisor is an Object of Type ActivityStackSupervisor through which all activitystacks are run.

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

    final 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 config,
            Bundle options, boolean ignoreTargetSecurity, int userId,
            IActivityContainer iContainer, TaskRecord inTask) {
        // Reject possible leak file descriptors
        if(intent ! =null && intent.hasFileDescriptors()) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }
        booleancomponentSpecified = intent.getComponent() ! =null;

        // Do not modify the client object!
        intent = new Intent(intent);

        // Collect information about the Intent target.
        ActivityInfo aInfo =
                resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);

        ActivityContainer container = (ActivityContainer)iContainer;
        synchronized (mService) {
            if(container ! =null&& container.mParentActivity ! =null&& container.mParentActivity.state ! = RESUMED) {// If the parent Activity is not resumed, the child Activity cannot be started.
                returnActivityManager.START_CANCELED; }...int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho,
                    requestCode, callingPid, callingUid, callingPackage,
                    realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
                    componentSpecified, null, container, inTask); .returnres; }}Copy the code

The startActivityLocked code is very verbose. With the system_process Log, we can confirm that:

userId = 0

Intent. ToShortString (true, true, true, false) for CMP = com. The demo. The framework /. DemoActivity

pid = 10124

I/ActivityManager: START u0 {cmp=com.demo.framework/.DemoActivity} from uid 10124 on display 0

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

    final int startActivityLocked(IApplicationThread caller,
            Intent intent, String resolvedType, ActivityInfo aInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode,
            int callingPid, int callingUid, String callingPackage,
            int realCallingPid, int realCallingUid, int startFlags, Bundle options,
            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
            ActivityContainer container, TaskRecord inTask) {...if (err == ActivityManager.START_SUCCESS) {
            Slog.i(TAG, "START u" + userId + "{" + intent.toShortString(true.true.true.false)
                    + "} from uid " + callingUid
                    + " on display " + (container == null ? (mFocusedStack == null ?
                            Display.DEFAULT_DISPLAY : mFocusedStack.mDisplayId) :
                            (container.mActivityDisplay == null? Display.DEFAULT_DISPLAY : container.mActivityDisplay.mDisplayId))); }.../ / create ActivityRecord
        ActivityRecord r = newActivityRecord(mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, requestCode, componentSpecified, voiceSession ! =null.this, container, options); . err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,true, options, inTask); .return err;
    }
Copy the code

Into startActivityUncheckedLocked function

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
            boolean doResume, Bundle options, TaskRecord inTask) {...// Check the Activity startup mode. None of these are configured in Demo
        final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
        final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
        final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;

        int launchFlags = intent.getFlags();
        if((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ! =0 &&
                (launchSingleInstance || launchSingleTask)) {
            // This branch is not satisfied. }else {
            // Set the corresponding launchFlags
            switch (r.info.documentLaunchMode) {
                case ActivityInfo.DOCUMENT_LAUNCH_NONE:
                    break;
                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
                    break;
                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
                    break;
                case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
                    launchFlags &= ~Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
                    break; }}// Background start task?
        final booleanlaunchTaskBehind = r.mLaunchTaskBehind && ! launchSingleTask && ! launchSingleInstance && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ! =0; .// If we are actually starting a new task, then in some cases we want to do multiple tasks.
        if((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) ! =0) {
            if(launchTaskBehind || r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) { launchFlags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK; }}...// If the caller is not from another Ativity, but gives us an explicit task and they want us to activate the new Ativity, let's take a look.
        if (sourceRecord == null&& inTask ! =null&& inTask.stack ! =null) {... }else {
            inTask = null;
        }

        ActivityInfo newTaskInfo = null;
        Intent newTaskIntent = null; ActivityStack sourceStack; .boolean movedHome = false;
        ActivityStack targetStack;

        intent.setFlags(launchFlags);
        final booleannoAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) ! =0;

        // We might want to try to place the new Ativity into the existing task.
        If the target Ativity is singleTask or singleInstance, we always do this;
        // If NEW_TASK has been requested, we will do this as well, and no other qualifier tells us to still place it in the new task:
        // Multitask, always in DOC mode, or be asked to start it as a new task after the current task.
        if(((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) ! =0 &&
                (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
                || launchSingleInstance || launchSingleTask) {
            ......
        }

        if(r.packageName ! =null) {
            // If the Activity being started is the same as the Activity currently at the top, we need to check that it should only be started once.
            ActivityStack topStack = mFocusedStack;
            ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
            if(top ! =null && r.resultTo == null) {
                if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
                    if(top.app ! =null&& top.app.thread ! =null) {
                        if((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) ! =0|| launchSingleTop || launchSingleTask) { ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task); .return ActivityManager.START_DELIVERED_TO_TOP;
                        }
                    }
                }
            }

        } else {
            if(r.resultTo ! =null&& r.resultTo.task.stack ! =null) {
                r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho,
                        r.requestCode, Activity.RESULT_CANCELED, null);
            }
            ActivityOptions.abort(options);
            return ActivityManager.START_CLASS_NOT_FOUND;
        }

        boolean newTask = false;
        boolean keepCurTransition = false; TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord ! =null ?
                sourceRecord.task : null;

        // Should this be considered a new task?
        if (r.resultTo == null && inTask == null&&! addingToTask && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) ! =0) {... }else if(sourceRecord ! =null) {
            final TaskRecord sourceTask = sourceRecord.task;
            if (isLockTaskModeViolation(sourceTask)) {
                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
            }
            targetStack = sourceTask.stack;
            targetStack.moveToFront("sourceStackToFront");
            final TaskRecord topTask = targetStack.topTask();
            if(topTask ! = sourceTask) { targetStack.moveTaskToFrontLocked(sourceTask, noAnimation, options, r.appTimeTracker,"sourceTaskToFront");
            }
            if(! addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) ! =0) {
                // In this case, we add an Activity to an existing task, but if the Activity is already running, we ask the caller to clear the task.
                ActivityRecord top = sourceTask.performClearTaskLocked(r, launchFlags);
                keepCurTransition = true;
                if(top ! =null) {...returnActivityManager.START_DELIVERED_TO_TOP; }}else if(! addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) ! =0) {
                // In this case, we will start an Activity in our task,
                // The Activity may already be running somewhere in the history, if so, we want to put it at the top of the stack.
                final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r);
                if(top ! =null) {...returnActivityManager.START_DELIVERED_TO_TOP; }}// The existing Activity is starting the new Activity, so we want to keep the new Activity in the same task as the task that started it.
            r.setTask(sourceTask, null);
            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
                    + " in existing task " + r.task + " from source " + sourceRecord);

        } else if(inTask ! =null) {
            // The caller asks to start a new Activity in the explicit task provided to us.
            if (isLockTaskModeViolation(inTask)) {
                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
            }
            targetStack = inTask.stack;
            targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, r.appTimeTracker,
                    "inTaskToFront");

            // Check whether we should actually start the new Activity in the task or just reuse the current Activity at the top.
            ActivityRecord top = inTask.getTopActivity();
            if(top ! =null && top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
                if((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) ! =0
                        || launchSingleTop || launchSingleTask) {
                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
                    if((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) ! =0) {
                        We don't need to start a new Activity
                        return ActivityManager.START_RETURN_INTENT_TO_CALLER;
                    }
                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
                    returnActivityManager.START_DELIVERED_TO_TOP; }}if(! addingToTask) {// We don't actually want to add this Activity to the task, so we just stop here, but still tell the caller that we already used the Intent.
                ActivityOptions.abort(options);
                return ActivityManager.START_TASK_TO_FRONT;
            }

            r.setTask(inTask, null);
            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
                    + " in explicit task " + r.task);

        } else {
            // This does not start with an existing Activity, nor is it part of a new task... Just put it at the top of the task stack, although that would never happen these days.
            targetStack = computeStackFocus(r, newTask);
            targetStack.moveToFront("addingToTopTask"); ActivityRecord prev = targetStack.topActivity(); r.setTask(prev ! =null ? prev.task : targetStack.createTaskRecord(getNextTaskId(),
                            r.info, intent, null.null.true), null);
            mWindowManager.moveTaskToTop(r.task.taskId);
            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
                    + " in new guessed " + r.task);
        }

        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
                intent, r.getUriPermissionsLocked(), r.userId);

        if(sourceRecord ! =null && sourceRecord.isRecentsActivity()) {
            r.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
        }
        if (newTask) {
            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
        }
        ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
        targetStack.mLastPausedActivity = null;
        targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
        if(! launchTaskBehind) {// Don't set focus on an activity that's going to the back.
            mService.setFocusedActivityLocked(r, "startedActivity");
        }
        return ActivityManager.START_SUCCESS;
    }
Copy the code

TargetStack is of type ActivityStack, which is the stack of tasks that manage the Activity. It then calls its startActivityLocked function.

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

    final void startActivityLocked(ActivityRecord r, boolean newTask,
            boolean doResume, boolean keepCurTransition, Bundle options) {
        TaskRecord rTask = r.task;
        final inttaskId = rTask.taskId; . TaskRecord task =null;
        if(! newTask) {// Start searching from an existing task stack
            boolean startIt = true;
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                task = mTaskHistory.get(taskNdx);
                if (task.getTopActivity() == null) {
                    // All activities in the task have ended
                    continue;
                }
                if (task == r.task) {
                    // I found it! Now, if it is not already visible to the user, it can be added without boot; It launches 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()); task.addActivityToTop(r); r.putInHistory(); mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) ! =0, r.userId, r.info.configChanges, task.voiceSession ! =null,
                                r.mLaunchTaskBehind);
                        if (VALIDATE_TOKENS) {
                            validateAppTokensLocked();
                        }
                        ActivityOptions.abort(options);
                        return;
                    }
                    break;
                } else if (task.numFullscreen > 0) {
                    startIt = false; }}}// Place the new Activity at the top of the task stack to interact with the user.

        // If we don't place the new Activity first, we don't want to pass the onUserLeaving callback to the actual first Activity
        if(task == r.task && mTaskHistory.indexOf(task) ! = (mTaskHistory.size() -1)) {
            mStackSupervisor.mUserLeaving = false;
            if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
                    "startActivity() behind front, mUserLeaving=false");
        }

        task = r.task;

        // Put the Activity on the history stack and continue
        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
                new RuntimeException("here").fillInStackTrace());
        task.addActivityToTop(r);
        task.setFrontOfTask();

        r.putInHistory();
        if(! isHomeStack() || numActivities() >0) {
            // If we want to switch to a new task, or if the process of the next Activity is not currently running, we want to display the launch preview window.
            boolean showStartingIcon = newTask;
            ProcessRecord proc = r.app;
            if (proc == null) {
                proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid);
            }
            if (proc == null || proc.thread == null) {
                showStartingIcon = true;
            }
            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                    "Prepare open transition: starting " + r);
            if((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) ! =0) {
                mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, keepCurTransition);
                mNoAnimActivities.add(r);
            } else{ mWindowManager.prepareAppTransition(newTask ? r.mLaunchTaskBehind ? AppTransition.TRANSIT_TASK_OPEN_BEHIND : AppTransition.TRANSIT_TASK_OPEN : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition); mNoAnimActivities.remove(r); } mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) ! =0, r.userId, r.info.configChanges, task.voiceSession ! =null, r.mLaunchTaskBehind);
            boolean doShow = true;
            if (newTask) {
                // Even though the Activity has just started, we still need to reset it,
                // To make sure we apply affinity to move existing activities from any other task into it.
                // If the caller has requested to reset the target task, perform this operation.
                if((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) ! =0) {
                    resetTaskIfNeededLocked(r, r);
                    doShow = topRunningNonDelayedActivityLocked(null) == r; }}else if(options ! =null && new ActivityOptions(options).getAnimationType()
                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
                doShow = false;
            }
            if (r.mLaunchTaskBehind) {
                // Do not make a start window for mLaunchTaskBehind. More importantly, make sure we tell WindowManager that r is visible even if it is at the end of the task stack.
                mWindowManager.setAppVisibility(r.appToken, true);
                ensureActivitiesVisibleLocked(null.0);
            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                // Determine if we want to transition from another Activity with the same launch icon as the next Activity.
                // This allows the window manager to retain the previous window it created (if it still exists).
                ActivityRecord prev = mResumedActivity;
                if(prev ! =null) {
                    // We do not want to reuse the previous start preview in the following cases:
                    // (1) The Activity is currently in a different task.
                    if(prev.task ! = r.task) { prev =null;
                    }
                    // (2) The current Activity is displayed.
                    else if (prev.nowVisible) {
                        prev = null; } } mWindowManager.setAppStartingWindow( r.appToken, r.packageName, r.theme, mService.compatibilityInfoForPackageLocked( r.info.applicationInfo), r.nonLocalizedLabel, r.labelRes, r.icon, r.logo, r.windowFlags, prev ! =null ? prev.appToken : null, showStartingIcon);
                r.mStartingWindowShown = true; }}else {
            // If this is the first Activity, do not do any animationmWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) ! =0, r.userId, r.info.configChanges, task.voiceSession ! =null, r.mLaunchTaskBehind);
            ActivityOptions.abort(options);
            options = null;
        }
        if (VALIDATE_TOKENS) {
            validateAppTokensLocked();
        }

        if (doResume) {
            mStackSupervisor.resumeTopActivitiesLocked(this, r, options); }}Copy the code

Then call resumeTopActivitiesLocked ActivityStackSupervisor object method. This function calls resumeTopActivityLocked on targetStack.

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target, Bundle targetOptions) {
        if (targetStack == null) {
            targetStack = mFocusedStack;
        }
        // Process the targetStack first
        boolean result = false;
        if (isFrontStack(targetStack)) {
            result = targetStack.resumeTopActivityLocked(target, targetOptions);
        }

        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                if (stack == targetStack) {
                    // It has already been handled above
                    continue;
                }
                if (isFrontStack(stack)) {
                    stack.resumeTopActivityLocked(null); }}}return result;
    }
Copy the code

Make sure the topmost Activity in ActivityStack has resumed. The inResumeTopActivity variable prevents recursively entering resumeTopActivityLocked().

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
        if (mStackSupervisor.inResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }

        boolean result = false;
        try {
            // Protect against recursion.
            mStackSupervisor.inResumeTopActivity = true;
            if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {
                mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;
                mService.updateSleepIfNeededLocked();
            }
            result = resumeTopActivityInnerLocked(prev, options);
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }
        return result;
    }
Copy the code

The next step resumeTopActivityInnerLocked function called.

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {...// If we are currently suspending an Activity, do nothing before then.
        if(! mStackSupervisor.allPausedActivitiesComplete()) {if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
                    "resumeTopActivityLocked: Skip resume: some activity pausing.");
            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
            return false; }... mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);// We need to start suspending the current Activity to resume the Activity at the top of the stack...
        booleandontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) ! =0;
        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
        if(mResumedActivity ! =null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false.true, dontWaitForPause); }...return true;
    }
Copy the code

Start Pauses the current Activity in order to start a new Activity.

Start pausing the currently resumed activity. It is an error to call this if there is already an activity being paused or there is no resumed activity.

The comments for this function indicate that this function is responsible for suspending the Activity that is currently in the resume state.

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
            boolean dontWait) {... ActivityRecord prev = mResumedActivity;if (prev == null) {
            if(! resuming) { Slog.wtf(TAG,"Trying to pause when nothing is resumed");
                mStackSupervisor.resumeTopActivitiesLocked();
            }
            return false;
        }

        if (mActivityContainer.mParentActivity == null) {
            // Top level stack, not a child. Look for child stacks.
            mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming, dontWait);
        }

        if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSING: " + prev);
        else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
        mResumedActivity = null; mPausingActivity = prev; mLastPausedActivity = prev; mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) ! =0|| (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) ! =0 ? prev : null;
        prev.state = ActivityState.PAUSING;
        prev.task.touchActiveTime();
        clearLaunchTime(prev);
        final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
        if (mService.mHasRecents && (next == null|| next.noDisplay || next.task ! = prev.task || uiSleeping)) { prev.updateThumbnailLocked(screenshotActivities(prev),null);
        }
        stopFullyDrawnTraceIfNeeded();

        mService.updateCpuStats();

        if(prev.app ! =null&& prev.app.thread ! =null) {
            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
            try {
                EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                        prev.userId, System.identityHashCode(prev),
                        prev.shortComponentName);
                mService.updateUsageStats(prev, false);
                // Suspend the previous Activity
                prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags, dontWait);
            } 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.isSleepingOrShuttingDown()) { mStackSupervisor.acquireLaunchWakelock(); }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) { prev.pauseKeyDispatchingLocked(); }else if (DEBUG_PAUSE) {
                 Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off");
            }

            if (dontWait) {
                // Finish the Activity immediately
                completePauseLocked(false);
                return false;

            } else {
                // Schedule a pause timeout in case the app doesn't respond.
                // We don't give it much time because this directly impacts the
                // responsiveness seen by the user.
                Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
                msg.obj = prev;
                prev.pauseTime = SystemClock.uptimeMillis();
                mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
                return true; }}else{...return false; }}Copy the code

Prev is an ActivityRecord object, app is a ProcessRecord object, and Thread is an ApplicationThreadProxy object. It then calls its schedulePauseActivity method.

frameworks/base/core/java/android/app/ApplicationThreadNative.java

class ApplicationThreadProxy implements IApplicationThread {...public final void schedulePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IApplicationThread.descriptor);
        data.writeStrongBinder(token);
        data.writeInt(finished ? 1 : 0);
        data.writeInt(userLeaving ? 1 :0);
        data.writeInt(configChanges);
        data.writeInt(dontReport ? 1 : 0);
        mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); }... }Copy the code

The Binder mechanism allows you to invoke the schedulePauseActivity in the remote method ApplicationThread. This function simply sends a message.

frameworks/base/core/java/android/app/ActivityThread.java

    private class ApplicationThread extends ApplicationThreadNative {...public final void schedulePauseActivity(IBinder token, boolean finished,
                boolean userLeaving, int configChanges, boolean dontReport) {
            sendMessage(
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    token,
                    (userLeaving ? 1 : 0) | (dontReport ? 2 : 0), configChanges); }... }Copy the code

The actual sending of the message is done through an mH object, which is of type H.

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread {

    final H mH = new H();
    
    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); }... }Copy the code

The handleMessage method in the H object handles incoming messages.

frameworks/base/core/java/android/app/ActivityThread.java

    private class H extends Handler {...public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
            
                ......
                
                case PAUSE_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
                            (msg.arg1&2) != 0);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break; . }}Copy the code

The handlePauseActivity method is finally called in the PAUSE_ACTIVITY message processing case.

frameworks/base/core/java/android/app/ActivityThread.java

    private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport) {
        ActivityClientRecord r = mActivities.get(token);
        if(r ! =null) {
            //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
            if (userLeaving) {
                performUserLeavingActivity(r);
            }

            r.activity.mConfigChangeFlags |= configChanges;
            performPauseActivity(token, finished, r.isPreHoneycomb());

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

            // Tell the Activity Manager that we have paused
            if(! dontReport) {try {
                    ActivityManagerNative.getDefault().activityPaused(token);
                } catch (RemoteException ex) {
                }
            }
            mSomeActivitiesChanged = true; }}// Get the ActivityClientRecord type r object based on the token and call the overloaded version function
    final Bundle performPauseActivity(IBinder token, boolean finished,
            boolean saveState) {
        ActivityClientRecord r = mActivities.get(token);
        returnr ! =null ? performPauseActivity(r, finished, saveState) : null;
    }

    final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
            boolean saveState) {
        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;
        }
        try {
            // Next let the Activity save its current state and manage dialog boxes...
            if(! r.activity.mFinished && saveState) { callCallActivityOnSaveInstanceState(r); }// Now we are idle.
            r.activity.mCalled = false;
            mInstrumentation.callActivityOnPause(r.activity);
            EventLog.writeEvent(LOG_AM_ON_PAUSE_CALLED, UserHandle.myUserId(),
                    r.activity.getComponentName().getClassName());
            if(! r.activity.mCalled) {throw new SuperNotCalledException(
                    "Activity " + r.intent.getComponent().toShortString() +
                    " 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 "
                        + r.intent.getComponent().toShortString()
                        + ":" + e.toString(), e);
            }
        }
        r.paused = true;

        // Notify any outstanding on paused listeners
        ArrayList<OnActivityPausedListener> listeners;
        synchronized (mOnPauseListeners) {
            listeners = mOnPauseListeners.remove(r.activity);
        }
        intsize = (listeners ! =null ? listeners.size() : 0);
        for (int i = 0; i < size; i++) {
            listeners.get(i).onPaused(r.activity);
        }

        return! r.activity.mFinished && saveState ? r.state :null;
    }
Copy the code

Then entered the mInstrumentation. CallActivityOnPause (state Richard armitage ctivity) method.

frameworks/base/core/java/android/app/Instrumentation.java

    /* Perform calling of an activity's {@link Activity#onPause} method. The * default implementation simply calls through to that method. * * @param activity The activity being paused. */
    public void callActivityOnPause(Activity activity) {
        activity.performPause();
    }
Copy the code

Executes a call to the activity’s {Activity# onPause} method. The default implementation simply calls the method. How nice is the onPause() inside the function, which we often override in our activities.

frameworks/base/core/java/android/app/Activity.java

    final void performPause(a) {
        mDoReportFullyDrawn = false;
        mFragments.dispatchPause();
        mCalled = false;
        onPause();
        mResumed = false;
        if(! mCalled && getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.GINGERBREAD) {throw new SuperNotCalledException(
                    "Activity " + mComponent.toShortString() +
                    " did not call through to super.onPause()");
        }
        mResumed = false;
    }
Copy the code

When an Activity is paused, AMS is notified that the Activity has been paused.

frameworks/base/core/java/android/app/ActivityThread.java

    private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport) {
        ActivityClientRecord r = mActivities.get(token);
        if(r ! =null) {...// Log specifies dontReport=false to tell the Activity Manager that we have paused
            if(! dontReport) {try {
                    ActivityManagerNative.getDefault().activityPaused(token);
                } catch (RemoteException ex) {
                }
            }
            mSomeActivitiesChanged = true; }}Copy the code

ActivityManagerNative. GetDefault () get ActivityManagerProxy object, through the mechanism of Binder call AMS activityPaused method.

frameworks/base/core/java/android/app/ActivityManagerNative.java

class ActivityManagerProxy implements IActivityManager
{...public void activityPaused(IBinder token) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(token);
        mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); }... }Copy the code

Here is the activityPaused method in AMS.

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    @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 further calls activityPausedLocked methods of ActivityStack. The final call inside this method completePauseLocked completes the pause.

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

    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)"));
                completePauseLocked(true);
            } else{ EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, r.userId, System.identityHashCode(r), r.shortComponentName, mPausingActivity ! =null
                            ? mPausingActivity.shortComponentName : "(none)");
                if (r.finishing && r.state == ActivityState.PAUSING) {
                    if (DEBUG_PAUSE) Slog.v(TAG,
                            "Executing finish of failed to pause activity: " + r);
                    finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false); }}}}private void completePauseLocked(boolean resumeNext) {...if (resumeNext) {
            final ActivityStack topStack = mStackSupervisor.getFocusedStack();
            if(! mService.isSleepingOrShuttingDown()) { mStackSupervisor.resumeTopActivitiesLocked(topStack, prev,null);
            } else {
                mStackSupervisor.checkReadyForSleepLocked();
                ActivityRecord top = topStack.topRunningActivityLocked(null);
                if (top == null|| (prev ! =null&& top ! = prev)) {// If there are no more activities to run, resume anyway to start some activities running.
                    mStackSupervisor.resumeTopActivitiesLocked(topStack, null.null); }}}... }Copy the code

Will turn into ActivityStackSupervisor resumeTopActivitiesLocked to call. After some calls (ditto) ended up in ActivityStack class resumeTopActivityInnerLocked function call mStackSupervisor. StartSpecificActivityLocked (next, true, true)

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {...if(next.app ! =null&& next.app.thread ! =null) {... }else {
            // Whoops, need to restart this activity!
            if(! next.hasBeenLaunched) { next.hasBeenLaunched =true;
            } else {
                if (SHOW_APP_STARTING_PREVIEW) {
                    mWindowManager.setAppStartingWindow(
                            next.appToken, next.packageName, next.theme,
                            mService.compatibilityInfoForPackageLocked(
                                    next.info.applicationInfo),
                            next.nonLocalizedLabel,
                            next.labelRes, next.icon, next.logo, next.windowFlags,
                            null.true);
                }
                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
            }
            if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
            mStackSupervisor.startSpecificActivityLocked(next, true.true);
        }

        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
        return true;
    }
Copy the code

Next call realStartActivityLocked.

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

    void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is the application of this activity already running?
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        r.task.stack.setLaunchTime(r);

        if(app ! =null&& app.thread ! =null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    // If it is a platform component marked to run in multiple processes, do not add this component,
                    Since it is actually part of the framework, it does not make sense to trace it as a separate APK in the process.
                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, 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.
        }

        mService.startProcessLocked(r.processName, r.info.applicationInfo, true.0."activity", r.intent.getComponent(), false.false.true);
    }
    
    final boolean realStartActivityLocked(ActivityRecord r,
            ProcessRecord app, boolean andResume, boolean checkConfig)
            throws RemoteException {... app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    newConfiguration(stack.mOverrideConfig), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, ! andResume, mService.isNextTransitionForward(), profilerInfo); . }Copy the code

Next, app is a ProcessRecord object, Thread is an ApplicationThreadProxy object, and its scheduleLaunchActivity method is called.

frameworks/base/core/java/android/app/ApplicationThreadNative.java

class ApplicationThreadProxy implements IApplicationThread {...public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
            ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
            CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
            int procState, Bundle state, PersistableBundle persistentState,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IApplicationThread.descriptor);
        intent.writeToParcel(data, 0);
        data.writeStrongBinder(token);
        data.writeInt(ident);
        info.writeToParcel(data, 0);
        curConfig.writeToParcel(data, 0);
        if(overrideConfig ! =null) {
            data.writeInt(1);
            overrideConfig.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        compatInfo.writeToParcel(data, 0); data.writeString(referrer); data.writeStrongBinder(voiceInteractor ! =null ? voiceInteractor.asBinder() : null);
        data.writeInt(procState);
        data.writeBundle(state);
        data.writePersistableBundle(persistentState);
        data.writeTypedList(pendingResults);
        data.writeTypedList(pendingNewIntents);
        data.writeInt(notResumed ? 1 : 0);
        data.writeInt(isForward ? 1 : 0);
        if(profilerInfo ! =null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); }... }Copy the code

The Binder mechanism allows us to invoke the scheduleLaunchActivity in the remote method ApplicationThread. This function simply sends a message.

frameworks/base/core/java/android/app/ActivityThread.java

    private class ApplicationThread extends ApplicationThreadNative {...@Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = newActivityClientRecord(); r.token = token; r.ident = ident; r.intent = intent; r.referrer = referrer; r.voiceInteractor = voiceInteractor; r.activityInfo = info; r.compatInfo = compatInfo; r.state = state; r.persistentState = persistentState; r.pendingResults = pendingResults; r.pendingIntents = pendingNewIntents; r.startsNotResumed = notResumed; r.isForward = isForward; r.profilerInfo = profilerInfo; r.overrideConfig = overrideConfig; updatePendingConfiguration(curConfig); sendMessage(H.LAUNCH_ACTIVITY, r); }... }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);
    }
    
    private class H extends Handler {...public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break; . }}... }private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        if(r.profilerInfo ! =null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }

        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null.null);

        if (localLOGV) Slog.v(
            TAG, "Handling launch of " + r);

        // Initialize before creating the activity
        WindowManagerGlobal.initialize();

        Activity a = performLaunchActivity(r, customIntent);

        if(a ! =null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed);if(! r.activity.mFinished && r.startsNotResumed) {// The activity manager actually wants this one to start out
                // paused, because it needs to be visible but isn't in the
                // foreground. We accomplish this by going through the
                // normal startup (because activities expect to go through
                // onResume() the first time they run, before their window
                // is displayed), and then pausing it. However, in this case
                // we do -not- need to do the full pause cycle (of freezing
                // and such) because the activity manager assumes it can just
                // retain the current state it has.
                try {
                    r.activity.mCalled = false;
                    mInstrumentation.callActivityOnPause(r.activity);
                    // 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;
                    }
                    if(! r.activity.mCalled) {throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " 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 "
                                + r.intent.getComponent().toShortString()
                                + ":" + e.toString(), e);
                    }
                }
                r.paused = true; }}else {
            // If there was an error, for any reason, tell the activity
            // manager to stop us.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null.false);
            } catch (RemoteException ex) {
                // Ignore}}}private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else{enter the branch mInstrumentation. CallActivityOnCreate (activity, r.s Tate); }... }Copy the code

The next step is to call the callActivityOnCreate method on the mInstrumentation object of the Instrumentation type.

frameworks/base/core/java/android/app/Instrumentation.java

    /**
     * Perform calling of an activity's {@link Activity#onCreate}
     * method.  The default implementation simply calls through to that method.
     *
     * @param activity The activity being created.
     * @param icicle The previously frozen state (or null) to pass through to onCreate().
     */
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }
Copy the code

Finally, go to performCreate for your Activity. He finally calls the onCreate method in the Activity.

frameworks/base/core/java/android/app/Activity.java

    final void performCreate(Bundle icicle) {
        restoreHasCurrentPermissionRequest(icicle);
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }
    
    @MainThread
    @CallSuper
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ":" + savedInstanceState);
        if(mLastNonConfigurationInstances ! =null) {
            mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
        }
        if(mActivityInfo.parentActivityName ! =null) {
            if (mActionBar == null) {
                mEnableDefaultActionBarUp = true;
            } else {
                mActionBar.setDefaultDisplayHomeAsUpEnabled(true); }}if(savedInstanceState ! =null) { Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); mFragments.restoreAllState(p, mLastNonConfigurationInstances ! =null
                    ? mLastNonConfigurationInstances.fragments : null);
        }
        mFragments.dispatchCreate();
        getApplication().dispatchActivityCreated(this, savedInstanceState);
        if(mVoiceInteractor ! =null) {
            mVoiceInteractor.attachActivity(this);
        }
        mCalled = true;
    }
Copy the code

Below is the debug trace Log, printed for each method name, plus some acronyms for the corresponding class.

AMN —- ActivityManagerNative, which is actually the internal ActivityManagerProxy for the call

AMS —- ActivityManagerService

ASS —- ActivityStackSupervisor

AS —- ActivityStack

ATP —- ApplicationThreadProxy

07-06 20:45:38.474 2356-2356/com.demo.framework D/lhw: AMN startActivity callingPackage = com.demo.framework,resolvedType = null,resultWho = null,requestCode = -1,startFlags = 0
07-06 20:45:38.475 771-1744/system_process I/lhw: AMS startActivity callingPackage = com.demo.framework,resolvedType = null,resultWho = null,requestCode = -1,startFlags = 0
07-06 20:45:38.475 771-1744/system_process I/lhw: AMS startActivityAsUser callingPackage = com.demo.framework,resolvedType = null,resultWho = null,requestCode = -1,startFlags = 0,userId = 0
07-06 20:45:38.476 771-1744/system_process I/lhw: ASS startActivityMayWait callingUid = -1,callingPackage = com.demo.framework,resolvedType = null,resultWho = null,requestCode = -1,startFlags = 0,userId = 0
07-06 20:45:38.476 771-1744/system_process I/lhw: ASS startActivityLocked callingUid = -1,callingPackage = com.demo.framework,resolvedType = null,callingPid = -1,realCallingPid = 2356,realCallingUid = 10124,resultWho = null,requestCode = -1,startFlags = 0
07-06 20:45:38.476 771-1744/system_process I/lhw: ASS startActivityUncheckedLocked doResume = true,startFlags = 0
07-06 20:45:38.477 771-1744/system_process I/lhw: AS startActivityLocked doResume = true,newTask = false,keepCurTransition = false
07-06 20:45:38.477 771-1744/system_process I/lhw: ASS resumeTopActivitiesLocked
07-06 20:45:38.477 771-1744/system_process I/lhw: AS resumeTopActivityLocked
07-06 20:45:38.477 771-1744/system_process I/lhw: AS resumeTopActivityInnerLocked
07-06 20:45:38.477 771-1744/system_process I/lhw: AS resumeTopActivityInnerLocked startPausingLocked start Pausing = ActivityRecord{75912a6 u0 com.demo.framework/.MainActivity t530},pausing=false
07-06 20:45:38.477 771-1744/system_process I/lhw: AS startPausingLocked userLeaving=true,uiSleeping=false,resuming=true,dontWait=false
07-06 20:45:38.477 771-1744/system_process I/lhw: AS startPausingLocked Enqueueing pending pause:ActivityRecord{75912a6 u0 com.demo.framework/.MainActivity t530}
07-06 20:45:38.477 771-1744/system_process I/lhw: ATP schedulePauseActivity finished=false,userLeaving=true,configChanges=0,dontReport=false
07-06 20:45:38.477 2356-2367/com.demo.framework I/lhw: ApplicationThread schedulePauseActivity finished=false,userLeaving=true,configChanges=0,dontReport=false
07-06 20:45:38.477 2356-2367/com.demo.framework I/lhw: ActivityThread sendMessage SCHEDULE 101 101: 1 / android.os.BinderProxy@c3122b9
07-06 20:45:38.478 771-1744/system_process I/lhw: AS resumeTopActivityInnerLocked startPausingLocked end pausing = true
07-06 20:45:38.480 2356-2356/com.demo.framework I/lhw: H handleMessage handling: 101
07-06 20:45:38.480 2356-2356/com.demo.framework I/lhw: H handleMessage PAUSE_ACTIVITY case
07-06 20:45:38.480 2356-2356/com.demo.framework I/lhw: ActivityThread handlePauseActivity finished=false,userLeaving=true,configChanges=0,dontReport=false
07-06 20:45:38.480 2356-2356/com.demo.framework I/lhw: ActivityThread performPauseActivity finished=false,saveState=false
07-06 20:45:38.480 2356-2356/com.demo.framework I/lhw: Instrumentation callActivityOnPause
07-06 20:45:38.480 2356-2356/com.demo.framework I/lhw: Activity performPause
07-06 20:45:38.480 2356-2356/com.demo.framework I/lhw: Activity onPause com.demo.framework.MainActivity@f9690ac
07-06 20:45:38.480 2356-2356/com.demo.framework I/lhw: ActivityManagerProxy activityPaused
07-06 20:45:38.481 771-1744/system_process V/lhw: AMS activityPaused: token=Token{6ffb4e7 ActivityRecord{75912a6 u0 com.demo.framework/.MainActivity t530}}
07-06 20:45:38.481 771-1744/system_process V/lhw: AS activityPausedLocked token=Token{6ffb4e7 ActivityRecord{75912a6 u0 com.demo.framework/.MainActivity t530}}, timeout=false
07-06 20:45:38.481 771-1744/system_process V/lhw: AS completePauseLocked resumeNext=true,Complete pause: ActivityRecord{75912a6 u0 com.demo.framework/.MainActivity t530}
07-06 20:45:38.481 771-1744/system_process I/lhw: ASS resumeTopActivitiesLocked
07-06 20:45:38.481 771-1744/system_process I/lhw: AS resumeTopActivityLocked
07-06 20:45:38.481 771-1744/system_process I/lhw: AS resumeTopActivityInnerLocked
07-06 20:45:38.481 771-1744/system_process I/lhw: AS resumeTopActivityLocked: Restarting ActivityRecord{99f7f24 u0 com.demo.framework/.DemoActivity t530}
07-06 20:45:38.481 771-1744/system_process I/lhw: ASS startSpecificActivityLocked andResume = true,checkConfig = true
07-06 20:45:38.481 771-1744/system_process I/lhw: ASS realStartActivityLocked andResume = true,checkConfig = true
07-06 20:45:38.482 771-1744/system_process I/lhw: ATP scheduleLaunchActivity referrer=com.demo.framework,notResumed=false,isForward=true,ident=161447716
07-06 20:45:38.483 2356-2394/com.demo.framework V/lhw: ApplicationThread scheduleLaunchActivity referrer=com.demo.framework,procState=2,notResumed=false,isForward=true
07-06 20:45:38.483 2356-2394/com.demo.framework I/lhw: ActivityThread sendMessage SCHEDULE 100 100: 0 / ActivityRecord{db690e token=android.os.BinderProxy@6422f2f {com.demo.framework/com.demo.framework.DemoActivity}}
07-06 20:45:38.489 2356-2356/com.demo.framework I/lhw: H handleMessage handling: 100
07-06 20:45:38.489 2356-2356/com.demo.framework I/lhw: H handleMessage LAUNCH_ACTIVITY case
07-06 20:45:38.489 2356-2356/com.demo.framework I/lhw: ActivityThread handleLaunchActivity r=ActivityRecord{db690e token=android.os.BinderProxy@6422f2f {com.demo.framework/com.demo.framework.DemoActivity}}
07-06 20:45:38.489 2356-2356/com.demo.framework I/lhw: ActivityThread performLaunchActivity r=ActivityRecord{db690e token=android.os.BinderProxy@6422f2f {com.demo.framework/com.demo.framework.DemoActivity}}
07-06 20:45:38.498 2356-2356/com.demo.framework I/lhw: Activity onCreate com.demo.framework.DemoActivity@f21b6c5: null
Copy the code

A sequence diagram is attached to conclude