The start process of an Activity starts another Activity in one Activity (based on api21)

Keywords: Android Activity


Based on the source code of Android 5.0, analyze the Start process of Activity.

1. startActivity() method of activity class

There are two methods for startActivity():

  • The first method is to directly call the startActivity() method in the Activity. The code is MainActivity.this.startActivity(intent). This method will directly call the startActivity() method in the Activity class,
    @Override
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);

And then called.

	@Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
       
            startActivityForResult(intent, -1);
        }
    }

And then called.

   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( // Send the result, that is, onActivityResult will be called 
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
        } else {
            ...
        }
    }

First, judge whether mParent == null is true. As the name suggests, mParent is the parent Activity of the current Activity, and our Activity is MainActivity, which has no parent Activity, so the result of mParent == null here is true. In fact, mParent is often used in ActivityGroup, and ActivityGroup has been abandoned (added in API level 1, deprecated since API level 13, replacing the API of Fragment and FragmentManager). After entering the if statement, take a look. The next step is the execStartActivity() method of the called Instrumentation

  • The second method is to directly call the startActivity() method of the Context class that is not an Activity. The code is getApplicationContext().startActivity(intent). This method will call the startActivity() method of the Context class, but this is an abstract method. Its implementation is in the ContextWrapper class, which is a wrapper class. It is estimated that it will not do any work. OK, let's take a look at the startActivity() of the Context class method:
	@Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }

The mBase here is actually a ContextImpl object, so continue to look at the startActivity(intent) method in the ContextImpl class

 	@Override
    public void startActivity(Intent intent) {
        warnIfCallingFromSystemProcess();
        startActivity(intent, null);
    }

And then called.

 	@Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
            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);
    }

It can be seen that the started Activity has no Activity stack, so it cannot be started in standard mode. The FLAG_ACTIVITY_NEW_TASK must be added, otherwise an exception will be reported
You can see that the final call is also the execStartActivity() method of Instrumentation

This time we analyze the first method:

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
MainActivity.this.startActivity(intent);

2. execStartActivity() method of instrumentation class

The Instrumentation class is used to monitor the interaction between the application and the system, and to assist the Activity to complete the process of starting the Activity
Take a look at its execStartActivity() method. Here are the parameters:

  • Context who = MainActivity.this,
  • IBinder contextThread = mMainThread.getApplicationThread(), which is an ApplicationThread object, where mMainThread is initialized in the attach() method of Activity
  • IBinder token = mToken, where mToken is also initialized in the attach() method of the Activity. This is an internal token used to identify the system that is starting the Activity. It may be null,
  • Activity target = MainActivity.this, which initiates the activity to be started
  • Intent intent = intent, i.e. new Intent(MainActivity.this, SecondActivity.class);
  • requestCode = -1,
  • options = null.
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        // The core function is completed in this whoThread, and its internal scheduleLaunchActivity method is used to open the activity 
        IApplicationThread whoThread = (IApplicationThread) contextThread; 
        // Here, list < ActivityMonitor > m activitymonitors is a collection of activitymonitors.
        // ActivityMonitor is a tool class added by Google for InstrumentationTest.
        // Therefore, if you don't test, you won't go into this branch.
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
            // Send a request to AMS through Binder to start the Activity
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            // Check result: if the Activity is started successfully, this method will not perform any operation; only when there is a problem, this method will throw an exception.
            // Take a look at this method: when we first learn it, we will probably see some exceptions.
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
        }
        return null;
    }

This calls the ActivityManagerNative.getDefault().startActivity() method. Enter the ActivityManagerNative class and see that ActivityManagerNative.getDefault() obtains an IActivityManager object

    static public IActivityManager getDefault() {
        return gDefault.get();
    }

gDefault is defined as follows:

  private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
			// Returns a reference to a service with the given name
            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);
            }
            return am;
        }
    };

In this method, a singleton is used to save the proxy object of ActivityManagerService. Therefore, the getDefault() method obtains the proxy object of ActivityManagerService

ActivityManagerNative class is an abstract class that inherits from Binder and implements the IActivityManager interface. There is also an ActivityManagerProxy class in the compilation unit of ActivityManagerNative, which also implements the IActivityManager interface. However, ActivityManagerProxy is not an internal class of ActivityManagerNative. What is the relationship between them?

IActivityManager is an IInterface, which represents the capabilities of remote services. ActivityManagerNative refers to Binder local objects (similar to the Stub class generated by AIDL tools) , this class is an abstract class, and its implementation is ActivityManagerService; therefore, the final operation of AMS will enter the real implementation of ActivityManagerService; there is a non-public class ActivityManagerProxy in the compilation unit of ActivityManagerNative.java, which represents the Binder proxy object. ActivityManager is a management class.

Calling the startActivity() method of the proxy object of ActivityManagerService actually calls the startActivity() method of ActivityManagerService

3. startActivity() method of activitymanagerservice class

  • Iaapplicationthread caller = the iaapplicationthread object obtained by type conversion of mmaintahread. Getapplicationthread(),
  • String callingPackage = current context.getBasePackageName()
  • Intent intent = intent, i.e. new Intent(MainActivity.this, SecondActivity.class);
  • String resolvedType, MIME type of intent
  • IBinder resultTo = mToken
  • String resultWho = mEmbeddedID. This value is assigned in the attach method of the Activity.
  • int requestCode = -1
  • int startFlags = 0
  • ProfilerInfo profilerInfo = null
  • Bundle options = null
    @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());
    }

Then, in this class, the startActivityAsUser method is invoked, only a new int userId parameter is added.

  • Iaapplicationthread caller, the iaapplicationthread object obtained by type conversion of mmaintahread. Getapplicationthread()
  • String callingPackage, current context.getBasePackageName()
  • Intent intent, intent, i.e. new Intent(MainActivity.this, SecondActivity.class);
  • String resolvedtype, MIME type of intent,
  • IBinder resultTo, mToken
  • String resultWho, mEmbeddedID. This value is assigned in the attach method of the Activity.
  • int requestCode, -1
  • int startFlags, 0
  • ProfilerInfo profilerInfo, null
  • Bundle options, null
  • int userId, UserHandle.getCallingUserId()
    /** Run all ActivityStacks through this */
    ActivityStackSupervisor mStackSupervisor;
    
    @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) {
        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
                false, ALLOW_FULL_ONLY, "startActivity", null);
        return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, options, userId, null, null); // Call here
    }

mStackSupervisor is an ActivityStackSupervisor object through which all activities run. Its initialization is in the constructor of ActivityManagerService. For details on ActivityStatck and ActivityStackSupervisor, you can see: ActivityRecord of four components.

4. startActivityMayWait() method of activitystacksupervisor class

The main task is to obtain the ActivityInfo information through resolveActivity. MayWait means that when there are multiple activities to choose from, resolveActivity will pop up to the user.

  • Iaapplicationthread caller, mmainthread. Getapplicationthread() type conversion iaapplicationthread object, ApplicationThreadProxy object
  • int callingUid, -1
  • String callingPackage, current context.getBasePackageName()
  • Intent intent, intent, i.e. new Intent(MainActivity.this, SecondActivity.class);
  • String resolvedType,
  • IVoiceInteractionSession voiceSession, null
  • IVoiceInteractor voiceInteractor, null
  • Ibinder resultto and mtoken are assigned in the attach method of MainActivity;
  • String resultWho,mEmbeddedID. This value is assigned in the attach method of MainActivity.
  • int requestCode,-1
  • int startFlags,0
  • ProfilerInfo profilerInfo,null
  • WaitResult outResult, null
  • Configuration config,null
  • Bundle options,null
  • int userId, useId
  • IActivityContainer iContainer, null
  • TaskRecord inTask,null
	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, int userId, IActivityContainer iContainer, TaskRecord inTask) {
       
        boolean componentSpecified = intent.getComponent() != null;

        // Don't modify the client's object!
        intent = new Intent(intent);

        // Collect information about the target of the Intent.
        // Call resolveActivity() to parse some information of the target Activity and save it in aInfo according to the intent parameter
        ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
                profilerInfo, userId); 

        ActivityContainer container = (ActivityContainer)iContainer;
        synchronized (mService) {

            final ActivityStack stack;
            // container is null, enter this branch
            if (container == null || container.mStack.isOnHomeDisplay()) {
                // It will be assigned here
                stack = getFocusedStack();
            } else {
                stack = container.mStack;
            }

            final long origId = Binder.clearCallingIdentity();
	    // We won't enter this if branch because we don't set the android:cantSaveState attribute on the < Application > node
            if (aInfo != null &&
                    (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
               ...
            }
	    // Call here
            int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho,
                    requestCode, callingPid, callingUid, callingPackage,
                    realCallingPid, realCallingUid, startFlags, options,
                    componentSpecified, null, container, inTask); 

            return res;
        }
    }

5. startActivityLocked() method of activitystacksupervisor class

The work of this method includes: checking the permissions of the caller, checking intent, checking permissions, and constructing the ActivityRecord object corresponding to the Activity to be started.

  • Iaapplicationthread caller, mmainthread. Getapplicationthread() type conversion iaapplicationthread object, ApplicationThreadProxy object
  • Intent intent, intent, i.e. new Intent(MainActivity.this, SecondActivity.class);
  • String resolvedType,
  • The ActivityInfo object obtained by calling resolveActivity in ActivityInfo aInfo, startActivityMayWait() method.
  • IVoiceInteractionSession voiceSession, null
  • IVoiceInteractor voiceInteractor, null
  • IBinder resultTo, mToken
  • String resultWho, mEmbeddedID. This value is assigned in the attach method of the Activity.
  • int requestCode, -1
  • int callingPid, -1
  • int callingUid, -1
  • String callingPackage, current context.getBasePackageName()
  • Int realcallingpid, value of binder. Getcallingpid()
  • Int realcallinguid, value of binder. Getcallinguid()
  • int startFlags, 0
  • Bundle options, null
  • boolean componentSpecified, true
  • ActivityRecord[] outActivity, null
  • Activitycontainer container, value of getfocusedstack()
  • TaskRecord inTask,null
   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 componentSpecified, ActivityRecord[] outActivity, ActivityContainer container,
            TaskRecord inTask) {
        int err = ActivityManager.START_SUCCESS;

        ProcessRecord callerApp = null;
        if (caller != null) {
            callerApp = mService.getRecordForAppLocked(caller); // Gets the process record object ProcessRecord object of the caller
            if (callerApp != null) {
                callingPid = callerApp.pid;
                callingUid = callerApp.info.uid;
            } else {
                Slog.w(TAG, "Unable to find app for caller " + caller
                      + " (pid=" + callingPid + ") when starting: "
                      + intent.toString());
                err = ActivityManager.START_PERMISSION_DENIED; // If the caller cannot be found, the permission denied flag is returned
            }
        }
        ActivityRecord sourceRecord = null;
        ActivityRecord resultRecord = null; // Declare a record of the result Activity in the stack
        if (resultTo != null) {
            // Get the ActivityRecord it points to according to the resultTo Binder object, that is, the ActivityRecord information of MainActivity
            sourceRecord = isInAnyStackLocked(resultTo); 
        }
        ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack; // Get the ActivityStack object of the task stack of the result Activity (understood as the manager of the task stack). Here, get null

        final int launchFlags = intent.getFlags();
	// I won't enter here because I didn't set intent.flag for intent_ ACTIVITY_ FORWARD_ Result this flag bit
        if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) { 
            ...
        }
	// Without this branch, I have components
        if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) { 
            err = ActivityManager.START_INTENT_NOT_RESOLVED;
            ...
        }
	// No, I have this SecondActivity class
        if (err == ActivityManager.START_SUCCESS && aInfo == null) { 
            err = ActivityManager.START_CLASS_NOT_FOUND;
            ...
        }
	// No, I didn't use voice session at all
        if (err == ActivityManager.START_SUCCESS && sourceRecord != null
                && sourceRecord.task.voiceSession != null) { 
            ...
        }
	// No, voiceSession is null
        if (err == ActivityManager.START_SUCCESS && voiceSession != null) { 
           ...
        }
	// No, error is still the initialized value
        if (err != ActivityManager.START_SUCCESS) { 
            ...
            return err;
        }
	// Permission check. I have no problem. I can start SecondActivity. I must have passed this level
        final int startAnyPerm = mService.checkPermission(
                START_ANY_ACTIVITY, callingPid, callingUid);
        final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
                callingUid, aInfo.applicationInfo.uid, aInfo.exported);
        if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {
            ...
        }
	// Abort means to abort; To cause to die prematurely; Stop, this should be false, otherwise you don't have to go down
	// Mmintentfirewall is the IntentFirewall object and the intention firewall of the system, which can intercept the intention to open
        boolean abort = !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                callingPid, resolvedType, aInfo.applicationInfo);
        // mService.mController is currently null and will not enter this branch
        if (mService.mController != null) {
            try {
                Intent watchIntent = intent.cloneFilter();
                // Judge whether the ActivityController intercepts the started Activity. System level application locks can be implemented from here
                abort |= !mService.mController.activityStarting(watchIntent,
                        aInfo.applicationInfo.packageName);
            } catch (RemoteException e) {
                mService.mController = null;
            }
        }

        if (abort) { // Don't go here
            ...
            return ActivityManager.START_SUCCESS;
        }
	// Create an ActivityRecord object to record an Activity.
        ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
                requestCode, componentSpecified, this, container, options);
        if (outActivity != null) { // outActivity here is null
            outActivity[0] = r;
        }

        final ActivityStack stack = getFocusedStack();
        // The app is not switched, so it will not enter this branch. Refer to: https://blog.csdn.net/QQxiaoqiang1573/article/details/77015379
        if (voiceSession == null && (stack.mResumedActivity == null
                || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
            ...
        }
        doPendingActivityLaunchesLocked(false);
	// Finally call here.
        err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
                startFlags, true, options, inTask);
        return err;
    }

6. startActivityUncheckedLocked() method of activitystacksupervisor class

This method is used to set launchFlags

  • ActivityRecord r is the ActivityRecord object constructed in the startActivityLocked() method, that is, the ActivityRecord object corresponding to the SecondActivity
  • ActivityRecord sourceRecord is the ActivityReord object of MainActivity object
  • IVoiceInteractionSession voiceSession, null
  • IVoiceInteractor voiceInteractor, null
  • int startFlags,0
  • boolean doResume, true
  • Bundle options, null
  • TaskRecord inTask,null
    final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
            boolean doResume, Bundle options, TaskRecord inTask) {
        final Intent intent = r.intent;
        final int callingUid = r.launchedFromUid;
        
        if (inTask != null && !inTask.inRecents) {
            inTask = null;
        }

        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();
       
	// The options for constructing ActivityRecord are null, so r.mlunchtaskbehind is false and launchtaskbehind is false
        final boolean launchTaskBehind = r.mLaunchTaskBehind
                && !launchSingleTask && !launchSingleInstance
                && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0; 
	// Don't go here because intent.flag is not set_ ACTIVITY_ NEW_ TASK
        if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { 
            ...
        }
	// Do not take this branch because intent.flag is not set_ ACTIVITY_ NEW_ DOCUMENT
        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
            ...
        }
        // Don't go here because intent.flag is not set_ ACTIVITY_ NEW_ TASK
        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
            ...
        }

        mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;

        // This time, doResume is true, so it will not enter the if statement
        if (!doResume) { 
            r.delayedResume = true;
        }

        ActivityRecord notTop =
                (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; // null
        // This branch will not be taken because startFlags is 0
        if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
            ...
        }

        boolean addingToTask = false;
        TaskRecord reuseTask = null;

        // The current sourceRecord is not null, and the intask is null. We won't go here
        if (sourceRecord == null && inTask != null && inTask.stack != null) { 
            ...
        } else {
            inTask = null; // Get in here
        }

        if (inTask == null) {
            if (sourceRecord == null) {
                ...
            } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
            } else if (launchSingleInstance || launchSingleTask) {
                launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
            }
        }

        ActivityInfo newTaskInfo = null;
        Intent newTaskIntent = null;
        ActivityStack sourceStack;
        if (sourceRecord != null) {
            // sourceRecord does not finish and will not enter the following branch
            if (sourceRecord.finishing) {
                ...
            } else {
            	// When the caller ActivityRecord is not empty and is not in the finish state, assign a value to sourceStack
                sourceStack = sourceRecord.task.stack;
            }
        } else {
            ...
        }

        boolean movedHome = false;
        ActivityStack targetStack;
	// Set the previously configured lauchFlags to intent
        intent.setFlags(launchFlags); 

        // There is no flag in this call_ ACTIVITY_ NEW_ Task, I won't enter here
        if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
                (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
                || launchSingleInstance || launchSingleTask) { 
                ...
        }

	// The package name of the Activity to be started is not null. It is established. Go here
        if (r.packageName != null) { 
            ActivityStack topStack = getFocusedStack();
            ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
            // No, I started the SecondActivity instead of the current MainActivity
            if (top != null && r.resultTo == null) { 
                if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
                    ...
                }
            }
        } else { // I won't go here
            ...
        }

        boolean newTask = false;
        boolean keepCurTransition = false;

        TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
                sourceRecord.task : null;

        // This code is mainly used to obtain the value of ActivityStack targetStack.
        // I won't go here this time. inTask is null and intent.Flag is not set_ ACTIVITY_ NEW_ Task Flag
        if (r.resultTo == null && inTask == null && !addingToTask
                && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { 
            ...
        } else if (sourceRecord != null) { // Come here
            final TaskRecord sourceTask = sourceRecord.task;
            targetStack = sourceTask.stack; // The ActivityStack of the target points to the ActivityStack of the source
            targetStack.moveToFront();
            final TaskRecord topTask = targetStack.topTask();
            // An existing Activity is opening the new Activity, so put the new Activity in the same task stack as the Activity that started it. This is the effect I want this time. Go here
            r.setTask(sourceTask, null);
        } else if (inTask != null) { // Don't go here, the first if passed
           ...
        } else {  // Don't go here, the first if passed
           ...
        }
        ...   
        ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
        targetStack.mLastPausedActivity = null;
        // The last call here is called the startActivityLocked() method of the ActivityStack class.
        targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options); 
        
        return ActivityManager.START_SUCCESS;
    }

7. startActivityLocked() method of activitystack class

This method is used to stack the ActivityRecord object corresponding to the Activity to be started.

  • ActivityRecord r is the ActivityRecord object constructed in the startActivityLocked() method of ActivityStackSupervisor, that is, the ActivityRecord object corresponding to SecondActivity
  • boolean newTask, false
  • boolean doResume, true
  • boolean keepCurTransition, false
  • Bundle options,null
    final void startActivityLocked(ActivityRecord r, boolean newTask,
            boolean doResume, boolean keepCurTransition, Bundle options) {
        TaskRecord rTask = r.task;
        final int taskId = rTask.taskId;
      
        TaskRecord task = null;
        if (!newTask) { // Enter here and find the task.
            // If starting in an existing task, find where that is
            boolean startIt = true;
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { // Reverse traverse the collection of task stacks
                task = mTaskHistory.get(taskNdx);
                if (task.getTopActivity() == null) { // Empty stack, not what we want, skip
                    continue;
                }
                if (task == r.task) { // I found the task stack I wanted
                    break;
                } 
            }
        }

        task = r.task;
        
        task.addActivityToTop(r); // Insert the ActivityRecord object corresponding to the SecondActivity into the top of the stack
        task.setFrontOfTask(); // Mark the top-level ActivityRecord of the task stack

        r.putInHistory(); // The tag ActivityRecord has been placed in the host stack
        if (!isHomeStack() || numActivities() > 0) { // Enter this branch
            
            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 ((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_ON_LOCK_SCREEN) != 0, r.userId,
                    r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
            boolean doShow = true;
            if (newTask) { // false
                ...
            } else if (options != null && new ActivityOptions(options).getAnimationType()
                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
                doShow = false;
            }
            if (r.mLaunchTaskBehind) {
                mWindowManager.setAppVisibility(r.appToken, true);
                ensureActivitiesVisibleLocked(null, 0);
            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
               
                ActivityRecord prev = mResumedActivity;
                if (prev != null) {
                    if (prev.task != r.task) {
                        prev = null;
                    }
                    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 (doResume) { //If doResume is true, enter here
            mStackSupervisor.resumeTopActivitiesLocked(this, r, options); // Finally call here.
        }
    }

8. Resumetoperactivitieslocked() of activitystacksupervisor class

The purpose of this method is to ensure that the ActivityStack of the target is in the foreground.

  • Activitystack, targetstack, activitystack object
  • ActivityRecord target is the ActivityRecord object constructed in the startActivityLocked() method of ActivityStackSupervisor, that is, the ActivityRecord object corresponding to SecondActivity
  • Bundle targetOptions,null
    boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
            Bundle targetOptions) {
        if (targetStack == null) {
            targetStack = getFocusedStack();
        }
        // Do targetStack first.
        boolean result = false;
        if (isFrontStack(targetStack)) { // Return true and set it as the top level through task.setFrontOfTask()
            result = targetStack.resumeTopActivityLocked(target, targetOptions); // Call here
        }
        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) {
                    // Already started above
                    continue;
                }
                if (isFrontStack(stack)) {
                    stack.resumeTopActivityLocked(null);
                }
            }
        }
        return result;
    }

9. Resumetoperactivitylocked() method of activitystack class

It is mainly used to control the call of resumetoperactivityinnerlocked method to ensure that only one Activity executes resumetoperactivitylocked() operation at a time.

  • ActivityRecord prev is the ActivityRecord object constructed in the startActivityLocked() method of ActivityStackSupervisor, that is, the ActivityRecord object corresponding to SecondActivity
  • Bundle options, null
    final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
        if (inResumeTopActivity) { // This flag is used to prevent multiple calls to resumetoperactivityinnerlocked
            // Don't even start recursing
            return false;
        }

        boolean result = false;
        try {
            // Protect against recursion.
            inResumeTopActivity = true;
            result = resumeTopActivityInnerLocked(prev, options); // Call here
        } finally {
            inResumeTopActivity = false;
        }
        return result;
    }

10. Resumetoperactivityinnerlocked() method of activitystack class

  • ActivityRecord prev is the ActivityRecord object constructed in the startActivityLocked() method of ActivityStackSupervisor, that is, the ActivityRecord object corresponding to SecondActivity;
  • Bundle options, null
    final boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
        // If the system has not entered the booting or booted state, it is not allowed to start the Activity
        if (!mService.mBooting && !mService.mBooted) {
            // Not ready yet!  Not ready yet
            return false;
        }
        // parentActivity is not set here, so the following branches will not be entered
        ActivityRecord parent = mActivityContainer.mParentActivity;
        if ((parent != null && parent.state != ActivityState.RESUMED) ||
                !mActivityContainer.isAttachedLocked()) {
            // Do not resume this stack if its parent is not resumed.
            // TODO: If in a loop, make sure that parent stack resumeTopActivity is called 1st.
            return false;
        }
         
        cancelInitializingActivities();

        // Find the first activity that is not finishing. 
        // Find the first stack top Activity without finishing, that is, SecondActivity
        ActivityRecord next = topRunningActivityLocked(null);

        // Remember how we'll process this pause/resume situation, and ensure
        // that the state is reset however we wind up proceeding.
        // Remember how we handle this pause / resume situation and make sure that the state can be reset no matter how we end and continue processing.
        final boolean userLeaving = mStackSupervisor.mUserLeaving;
        mStackSupervisor.mUserLeaving = false;
        // Task stack TaskRecord object of ActivityRecord object corresponding to SecondActivity
        final TaskRecord prevTask = prev != null ? prev.task : null;
        // If you don't go here, next is not null.
        if (next == null) {
           ...
        }

        next.delayedResume = false;

        // If the top activity is the resumed one, nothing to do.
        // No, the top-level page is a new page. mResumedActivity is MainActivity and next is SecondActivity.
        if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
                    mStackSupervisor.allResumedActivitiesComplete()) {
           ...
        }

        final TaskRecord nextTask = next.task;
        // prevTask.isOverHomeStack() is false, so it will not enter this branch
        if (prevTask != null && prevTask.stack == this &&
                prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
            ...
        }

        // If we are sleeping, and there is no resumed activity, and the top
        // activity is paused, well that is the state we want.
        // This analysis will not enter this branch when the top activity is in sleep or shutdown state and has been suspended
        if (mService.isSleepingOrShuttingDown()
                && mLastPausedActivity == next
                && mStackSupervisor.allPausedActivitiesComplete()) {
           ...
        }

        // 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 the user with this activity does not start, it will return directly. This time, it will not enter this branch
        if (mService.mStartedUsers.get(next.userId) == null) {
            return false;
        }

        // The activity may be waiting for stop, but that is no longer
        // appropriate for it.
        // Activity may be waiting to stop, but that's no longer appropriate for it.
        mStackSupervisor.mStoppingActivities.remove(next);
        mStackSupervisor.mGoingToSleepActivities.remove(next);
        next.sleeping = false;
        mStackSupervisor.mWaitingVisibleActivities.remove(next);

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

        // If we are currently pausing an activity, then don't do anything
        // until that is done.
        // If we are pausing an activity, we will return directly. This analysis will not enter this branch.
        if (!mStackSupervisor.allPausedActivitiesComplete()) {
            return false;
        }

        // We need to start pausing the current activity so the top one
        // can be resumed... Wait until the current activity is suspended, and then resume top activity
        // The startup parameter of Activity contains FLAG_RESUME_WHILE_PAUSING
        // Indicates that the Resume operation can be performed simultaneously when the currently displayed Activity performs Pausing
        // If dontWaitForPause is true, it means that the top-level activity can resume without waiting for the current activity to complete pause
        boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0;
        // Pauses all activities in the background stack
        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
        // Migrate the Activity being displayed by the foreground ActivityStack to the pausing state
        if (mResumedActivity != null) {
            // If the activity in the current resumed state is not null, you need to pause the activity first
            // In this analysis, MainActivity is suspended.
            pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
            if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity);
        }
        // There is currently an Activity pausing. Pausing is true, so enter this branch
        if (pausing) {
            // Update lru list
            if (next.app != null && next.app.thread != null) {
                mService.updateLruProcessLocked(next.app, true, null);
            }
            // Note: this analysis is returned here, so there is no need to look down.
            return true;
        }
        // A lot of code is omitted
        ...
    }

11. startPausingLocked() method of activitystack class

  • boolean userLeaving false,
  • boolean uiSleeping false,
  • boolean resuming true,
  • boolean dontWait false.
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
        boolean dontWait) {
    // mPausingActivity is still null
    if (mPausingActivity != null) {
        Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity);
        completePauseLocked(false);
    }
    // The value of mResumedActivity is the ActivityRecord object corresponding to MainActivity, not null
    ActivityRecord prev = mResumedActivity;
    if (prev == null) { // Will not enter this branch
        ...
    }
    if (mActivityContainer.mParentActivity == null) {
        // Top level stack, not a child. Look for child stacks.
	// Pauses the activity of all child stacks
        mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming, dontWait);
    }
    // Clear the value of mResumedActivity
    mResumedActivity = null;
    // Assign the ActivityRecord corresponding to the MainActivity currently in the resumed state to mPausingActivity and mLastPausedActivity
    mPausingActivity = prev;
    mLastPausedActivity = prev;
    
    prev.state = ActivityState.PAUSING;
    prev.task.touchActiveTime();
    clearLaunchTime(prev);
    // Find the first stack top Activity without finishing, that is, the ActivityRecord object corresponding to the SecondActivity
    final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
    ...
    // prev is the ActivityRecord object, corresponding to MainActivity
    // prev.app is the ProcessRecord object
    // prev.app.thread is the iaapplicationthread object, which is used for system_server server processes communicate with client processes
    if (prev.app != null && prev.app.thread != null) {
        try {
           
            mService.updateUsageStats(prev, false);
            prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                    userLeaving, prev.configChangeFlags, dontWait);
        } catch (Exception e) {
            ...
        }
    } else {
        ...
    }
    
    // mPausingActivity is not null and will enter the if branch
    if (mPausingActivity != null) {
        ...
        if (dontWait) {
            ...
        } else { // dontWait is false, so enter the else branch
            // If no response from the app is received within 500ms, execute the method again.
            Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
            msg.obj = prev;
            prev.pauseTime = SystemClock.uptimeMillis();
            mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
            return true;
        }
    } else {
        ...
    }
}

prev.app.thread is actually an ApplicationThreadProxy object.

12. schedulePauseActivity() method of applicationthreadproxy

  • Ibinder token is the token of mainactivity,
  • boolean finished false,
  • boolean userLeaving false,
  • int configChanges,
  • boolean dontReport false
class ApplicationThreadProxy implements IApplicationThread {
    private final IBinder mRemote;
    
    public ApplicationThreadProxy(IBinder remote) {
        mRemote = remote;
    }
    
    public final IBinder asBinder() {
        return mRemote;
    }
    
    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();
    }
    ...
}

13. onTransact method of applicationthreadnative

public abstract class ApplicationThreadNative extends Binder
        implements IApplicationThread {

    static public IApplicationThread asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IApplicationThread in =
            (IApplicationThread)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
        
        return new ApplicationThreadProxy(obj);
    }
    
    public ApplicationThreadNative() {
        attachInterface(this, descriptor);
    }
    
    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case SCHEDULE_PAUSE_ACTIVITY_TRANSACTION:
        {
            data.enforceInterface(IApplicationThread.descriptor);
            IBinder b = data.readStrongBinder();
            boolean finished = data.readInt() != 0;
            boolean userLeaving = data.readInt() != 0;
            int configChanges = data.readInt();
            boolean dontReport = data.readInt() != 0;
            schedulePauseActivity(b, finished, userLeaving, configChanges, dontReport);
            return true;
        }

	...
        }

        return super.onTransact(code, data, reply, flags);
    }

    public IBinder asBinder()
    {
        return this;
    }
}

14. schedulePauseActivity() method of applicationthread class

  • Ibinder token is the token of mainactivity,
  • boolean finished false,
  • boolean userLeaving false,
  • int configChanges,
  • boolean dontReport false
public final class ActivityThread {
    final H mH = new H();
    final ApplicationThread mAppThread = new ApplicationThread();

    private class ApplicationThread extends ApplicationThreadNative {

        public final void schedulePauseActivity(IBinder token, boolean finished,
                boolean userLeaving, int configChanges, boolean dontReport) {
            sendMessage(
                    // finished is false, so h.pause is taken_ ACTIVITY
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    token, // obj is token
                    (userLeaving ? 1 : 0) | (dontReport ? 2 : 0), // arg1 is 0
                    configChanges); // args is configChanges
        }

    }

    private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }

    private void sendMessage(int what, Object obj, int arg1) {
        sendMessage(what, obj, arg1, 0, false);
    }

    private void sendMessage(int what, Object obj, int arg1, int arg2) {
        sendMessage(what, obj, arg1, arg2, false);
    }

    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }
}

15. handleMessage() method of class H

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

16. handlePauseActivity() method of activitythread class

  • Ibinder token is the token of mainactivity,
  • boolean finished false,
  • boolean userLeaving false,
  • int configChanges,
  • boolean dontReport false
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());
      
        // Tell the activity manager we have paused.
        if (!dontReport) { // dontReport is false, so it will enter this branch
            try {
                ActivityManagerNative.getDefault().activityPaused(token);
            } catch (RemoteException ex) {
            }
        }
        mSomeActivitiesChanged = true;
    }
}

16.1 performPauseActivity() method of activitythread class

  • Ibinder token is the token of mainactivity,
  • boolean finished false,
  • boolean saveState false
final Bundle performPauseActivity(IBinder token, boolean finished,
        boolean saveState) {
    ActivityClientRecord r = mActivities.get(token);
    return r != null ? performPauseActivity(r, finished, saveState) : null;
}
final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
        boolean saveState) {
    if (r.paused) { // Will not enter this branch
        ...
    }
    
    try {
        // Next have the activity save its current state and managed dialogs...
        if (!r.activity.mFinished && saveState) {
            callCallActivityOnSaveInstanceState(r);
        }
        // Now we are idle.
        r.activity.mCalled = false;
        // Callback the onPause method of the Activity through the Instrumentation object
        mInstrumentation.callActivityOnPause(r.activity);
        
    } catch (SuperNotCalledException e) {
        throw e;
    } catch (Exception e) {
        
    }
    r.paused = true;
    // 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);
    }
    return !r.activity.mFinished && saveState ? r.state : null;
}

17. activityPaused() method of activitymanagerservice

  • Ibinder token is the token of mainactivity,
@Override
public final void activityPaused(IBinder token) {
    final long origId = Binder.clearCallingIdentity();
    synchronized(this) {
        // Find the ActivityStack object according to the token
        ActivityStack stack = ActivityRecord.getStackLocked(token);
        if (stack != null) {
            stack.activityPausedLocked(token, false);
        }
    }
    Binder.restoreCallingIdentity(origId);
}

18. activityPausedLocked() method of activitystack class

  • Ibinder token is the token of mainactivity,
  • boolean timeout false.
final void activityPausedLocked(IBinder token, boolean timeout) {
    // According to the token, get the ActivityRecord object, that is, the ActivityRecord object corresponding to MainActivity.
    final ActivityRecord r = isInStackLocked(token);
    if (r != null) {
        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
        if (mPausingActivity == r) { // Enter this branch
            completePauseLocked(true);
        } else {
            ...
        }
    }
}

19. completePauseLocked() method of activitystack class

  • boolean resumeNext true
private void completePauseLocked(boolean resumeNext) {
    // mPausingActivity is the ActivityRecord object corresponding to MainActivity.
    ActivityRecord prev = mPausingActivity;
    if (prev != null) {
        prev.state = ActivityState.PAUSED;
        ...
        mPausingActivity = null;
    }
    if (resumeNext) {
        final ActivityStack topStack = mStackSupervisor.getFocusedStack();
        if (!mService.isSleepingOrShuttingDown()) { // Enter this branch
            mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
        } else {
            ...
        }
    }
    ...
}

20. Resumetoperactivitieslocked() method of activitystacksupervisor class

  • Activitystack, targetstack, activitystack object
  • ActivityRecord target is the ActivityRecord object corresponding to MainActivity
  • Bundle targetOptions,null
boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
        Bundle targetOptions) {
    if (targetStack == null) {
        targetStack = getFocusedStack();
    }
    // Do targetStack first.
    boolean result = false;
    if (isFrontStack(targetStack)) {
        // Call here
        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) {
                // Already started above.
                continue;
            }
            if (isFrontStack(stack)) {
                stack.resumeTopActivityLocked(null);
            }
        }
    }
    return result;
}

21. Resumetoperactivitylocked() method of activitystack class

It is mainly used to control the call of resumetoperactivityinnerlocked method to ensure that only one Activity executes resumetoperactivitylocked() operation at a time.

  • ActivityRecord prev is the ActivityRecord object corresponding to MainActivity
  • Bundle options, null
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
    if (inResumeTopActivity) {
        // Don't even start recursing.
        return false;
    }
    boolean result = false;
    try {
        // Protect against recursion.
        inResumeTopActivity = true;
        // Call here
        result = resumeTopActivityInnerLocked(prev, options);
    } finally {
        inResumeTopActivity = false;
    }
    return result;
}

22. Resumetoperactivityinnerlocked() method of activitystack class

  • ActivityRecord prev is the ActivityRecord object corresponding to MainActivity
  • Bundle options, null
final boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
   
    // Find the first activity that is not finishing.
    // next is the ActivityRecord object corresponding to SecondActivity
    ActivityRecord next = topRunningActivityLocked(null);
   
    final TaskRecord prevTask = prev != null ? prev.task : null;
    
    next.delayedResume = false;
    // mResumedActivity is null
    if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
                mStackSupervisor.allResumedActivitiesComplete()) {
       ...
    }
    final TaskRecord nextTask = next.task;
  
    
    // The activity may be waiting for stop, but that is no longer
    // appropriate for it.
    mStackSupervisor.mStoppingActivities.remove(next);
    mStackSupervisor.mGoingToSleepActivities.remove(next);
    next.sleeping = false;
    mStackSupervisor.mWaitingVisibleActivities.remove(next);
    
    // We need to start pausing the current activity so the top one
    // can be resumed...
    boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0;
    boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
    if (mResumedActivity != null) { // mResumedActivity is null and will not enter this branch
        pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
    }
    if (pausing) { // pausing is false
        ...
    }

    if (mService.isSleeping() && mLastNoHistoryActivity != null &&
            !mLastNoHistoryActivity.finishing) {
       ...
    }
    // prev is the ActivityRecord object corresponding to MainActivity
    // next is the ActivityRecord object corresponding to the SecondActivity, so enter the if branch
    if (prev != null && prev != next) {
        ... Omit non critical code
    }
   
    // We are starting up the next activity, so tell the window manager
    // that the previous one will be hidden soon.  This way it can know
    // to ignore it when computing the desired screen orientation.
    // We are opening the next activity, so tell wms that the previous activity will be hidden.
    boolean anim = true;
    if (prev != null) {
        if (prev.finishing) {
            ...
        } else { // Enter this branch
            ...Omit non critical code
        }
       
    } else {
        ...
    }
    ...
    ActivityStack lastStack = mStackSupervisor.getLastStack();
    // next.app is currently null, so it will not enter the if branch
    if (next.app != null && next.app.thread != null) {
      ...
    } else { // Entered the else branch
        if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Restarting " + next);
        // Finally called here
        mStackSupervisor.startSpecificActivityLocked(next, true, true);
    }
    return true;
}

23. startSpecificActivityLocked() method of activitystatcksupervisor class

  • ActivityRecord r is the ActivityRecord object constructed in the startActivityLocked() method of ActivityStackSupervisor, that is, the ActivityRecord object corresponding to SecondActivity
  • boolean andResume, true
  • boolean checkConfig,true
    void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        // Judge whether the application of the page is already running, and true will be returned here
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        r.task.stack.setLaunchTime(r);

        if (app != null && app.thread != null) {
            try {
                // Call here to really start the Activity
                realStartActivityLocked(r, app, andResume, checkConfig); 
                return;
            } catch (RemoteException e) {
            }
        }
    }

24. realStartActivityLocked() method of activitystatcksupervisor class

  • ActivityRecord r is the ActivityRecord object constructed in the startActivityLocked() method of ActivityStackSupervisor, that is, the ActivityRecord object corresponding to SecondActivity
  • ProcessRecord app, ProcessRecord object
  • boolean andResume, true
  • boolean checkConfig,true
    final boolean realStartActivityLocked(ActivityRecord r,
            ProcessRecord app, boolean andResume, boolean checkConfig)
            throws RemoteException {
        // Assign the ProcessRecord object to the ProcessRecord app member variable of the ActivityRecord.
        r.app = app;
        app.waitingToKill = null;
        r.launchCount++;
        r.lastLaunchTime = SystemClock.uptimeMillis();

        int idx = app.activities.indexOf(r);
        if (idx < 0) {
            app.activities.add(r);
        }

        final ActivityStack stack = r.task.stack;
        try {
            if (app.thread == null) {
                throw new RemoteException();
            }
            // Call here to start the Activity
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    r.compat, r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState,
                    results, newIntents, !andResume, mService.isNextTransitionForward(),
                    profilerInfo); 
        } catch (RemoteException e) {
           ...
        }

        r.launchFailed = false;
       
        if (andResume) {
            // As part of the process of launching, ActivityThread also performs
            // a resume. Update the activity information of the resumed status
            stack.minimalResumeActivityLocked(r);
        } else {
            ...
        }

        return true;
    }

app.thread here is an iaapplicationthread object, and here is the use of Binder. Iaapplicationthread inherits from IInterface and represents the capabilities of remote Service. ApplicationThreadNative refers to Binder local object and is an abstract class. Its implementation is ApplicationThread, and its location is in ActivityThread.java, It is an internal class of ActivityThread class. The real execution operation is in ApplicationThread

25. scheduleLaunchActivity() method of applicationthread class

Intent intent,
IBinder token,
int ident,
ActivityInfo info,
Configuration curConfig,
CompatibilityInfo compatInfo,
IVoiceInteractor voiceInteractor,
int procState,
Bundle state,
PersistableBundle persistentState,
List pendingResults,
List pendingNewIntents,
boolean notResumed, false
boolean isForward,
ProfilerInfo profilerInfo,

ApplicationThread is an internal class of ActivityThread.

	// we use token to identify this activity without having to send the
        // activity itself back to the activity manager. (matters more with ipc)
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
                IVoiceInteractor voiceInteractor, int procState, Bundle state,
                PersistableBundle persistentState, List<ResultInfo> pendingResults,
                List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
                ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            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;

            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r); // Call here to send a message to start the Activity
        }

Then call

    private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }

Then call

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

Here, mH is an H object. Class H inherits from the Handler class. See the message processing methods:

	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;
               	// Branches not related to analysis are omitted
            }
            if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
        }

Then call handleLaunchActivity, look at the name, process and start the Activity, which is getting closer and closer to the end

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

        Activity a = performLaunchActivity(r, customIntent); // Execute and start the Activity, where the Activity is created, and its onCreate() and onStart() will also be called

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            // Here, onResume() of the new Activity will be called
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed);

            if (!r.activity.mFinished && r.startsNotResumed) {
                try {
                    r.activity.mCalled = false;
                    mInstrumentation.callActivityOnPause(r.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
            }
        }
    }

Next, look at the performLaunchActivity method

		private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

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

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

        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            // Instrumentation creates an instance of the Activity using reflection
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());

            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.voiceInteractor);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                // onCreate of Activity will be called here
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
	                // Here, onStart() of Activity will be called
                    activity.performStart();
                    r.stopped = false;
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
                if (!r.activity.mFinished) {
                    activity.mCalled = false;
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onPostCreate()");
                    }
                }
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

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

        return activity;
    }

reference resources

Analysis of Android plug-in principle -- AMS & PMS of Hook mechanism

Android source code analysis - startup process of Activity

startActivity startup process analysis

Activity of four Android components - startup process (Part I)

Activity of four Android components - startup process (Part 2)

#Detailed explanation of ActivityRecord, TaskRecord, ActivityStack and Activity startup mode

Posted by DanAuito on Sun, 05 Dec 2021 18:21:55 -0800