• The Activity startup process (I) : The ActivityManagerService section
  • Start the Activity process (2) : the application process part

Activity is one of the most important components in Android, and almost all Android applications cannot do without the support of Activity. Therefore, it is very meaningful for us application layer developers to understand the startup process of activity and master the working principle of activity. In this article, the whole startup process of the activity has been sorted out, hoping to help you.

Note: This article is based on Android8.0.0 source code

1. An application process sends a request to the AMS

There are two main ways to start an Activity:

  • The first case is that the application process in which the Activity is to be started has not yet been created. For example, when we click on the icon of an application from the desktop to open the start Activity in the application, the Android system will create a new process because the application has not been started. You can then display the Activity page you want to launch.
  • The second case is that the process in which the Activity is to be started already exists. For example, if we click on a Button in the application to start another Activity in the application, as long as the target Activity is not specifically declared, a new Activity will be created in the current process without the need to start a new thread.

In either case, you actually start a new Activity by calling the Activity class’s startActivity method (or by calling the startActivityForResult method in the hope that the new page will return a result). Therefore, to clarify the process of starting an activity, start with the startActivity method.

The source of the startActivity method is as follows:

Source path: \frameworks\ Base \core\ Java \ Android \app\Activity.java

    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }
    
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if(options ! =null) {
            startActivityForResult(intent, -1, options);
        } else {
            startActivityForResult(intent, -1); }}Copy the code

As you can see, the startActivity method also ends up in the startActivityForResult method. startActivityForResult

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {.../ / comment 1
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); . }Copy the code

The code in comment 1, which is the core of the startActivityForResult method, hands the job of starting the activity to an Instrumentation object. Instrumentation is an intermediary between the application and the system. Let’s take a look at the execStartActivity method of Instrumentation:

Source path: \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; .intresult = ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! =null ? target.mEmbeddedID : null,
                        requestCode, 0.null, options); checkStartActivityResult(result, intent); . }Copy the code

Before reading the execStartActivity method, we need to take a look at the parameters that the method takes. Let’s compare the parameters that the method receives with those passed in when the method is called in the startActivityForResult method to see what they mean:

// The argument passed in when the call is made
execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);

// Method definition
execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options)
Copy the code

It is not hard to guess what these parameters mean by comparison:

  • Context WHO: This parameter is a Context object. ExecStartActivity is called with “this”, the current activity. This parameter represents the context of the initiator of the new activity.
  • IBinder contextThread: the parameters of the incoming is mMainThread getApplicationThread (), which is an IBinder object that represents the current application process.
  • IBinder Token: The initiator’s identifier that the system uses to identify who is starting a new activity. This parameter is passed in as the Activity’s member variable mToken, which is assigned when the Activity is created.
  • Activity Target: Represents the Activity that initiated the task to start a new Activity.
  • Intent Intent: The Intent we pass in when we call the startActivity method. It encapsulates the information that starts the activity.
  • Int requestCode: This parameter is required if you want to return results from the new page. If the value of this parameter is less than 0, the new page does not need to return results.
  • Bundle options: Additional data.

Now that you understand what these parameters mean, let’s look at the content of execStartActivity, Can see in the execStartActivity method mainly is to call the ActivityManager. GetService () startActivity to perform further launch activity. Let’s look at the ActivityManager. GetService () what is returned:

    public static IActivityManager getService(a) {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create(a) {
                
                    / / comment 2
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    returnam; }};Copy the code

By analyzing the source code, we can see that getService returns an IActivityManager object that uses the singleton pattern. In 2 place, first by ServiceManager. GetService to obtain an IBinder, Then through IActivityManager. Stub. AsInterface (b) this line of code to convert this Binder object to IActivityManager type. Should have learned about the implementation of an AIDL ways of students can’t tell, IActivityManager. Stub. AsInterface AIDL implementation is the standard, and access to the object of this IActivityManager here, It is the local proxy object for the system service ActivityManagerService.

ActivityManagerService (AMS for short) is a very important SystemService of Android. It is created when Android is started and managed by SystemService. AMS is mainly responsible for managing the activity page of each application in the Android system and controlling the life cycle of the activity object. Therefore, when our application process wants to start a new activity, it is natural to communicate with AMS. As you can see from the code above, post-8.0 systems use AIDL to communicate with AMS (note: pre-8.0 was not an AIDL).

When the ActivityManager. GetService () to get at the end of the this IActivityManager object, again call startActivity method of the object, as a result of this IActivityManager is actually AMS in a proxy for the client, So this is actually the AMS startActivity method being called. At this point, android startup moves from the application process to the AMS process.

2.AMS processes relevant requests

Let’s look at the source of the AMS startActivity method:

Source path: \ 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 bOptions) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }
    
    @Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {...return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null.null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null.null, bOptions, false, userId, null.null."startActivityAsUser");
    }
    
    
Copy the code

As you can see, the startActivityAsUser method is called in startActivityAsUser, and the startActivityMayWait method in mActivityStarter is called in startActivityAsUser. StartActivityMayWait method

Source path: \frameworks\ Base \services\core\ Java \com\ Android \server\am\ActivityStarterjava

    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 globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
            IActivityContainer iContainer, TaskRecord inTask, String reason) {...synchronized (mService) {
            ...
            final ActivityRecord[] outRecord = new ActivityRecord[1];
            intres = startActivityLocked(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity, componentSpecified, outRecord, container, inTask, reason); . }... }int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
            ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
            TaskRecord inTask, String reason) {... mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord, container, inTask); .return mLastStartActivityResult;
    }
Copy the code

The code for the startActivityMayWait method is long, but at the heart of the code is a call to startActivityLocked to further start the Activity, and within startActivityLocked the startActivity method is called, The startActivity method is as follows:

     private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
            ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
            TaskRecord inTask) {.../ / comment 1
            ActivityRecord r = newActivityRecord(mService, callerApp, callingPid, callingUid, callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, componentSpecified, voiceSession ! =null, mSupervisor, container, options, sourceRecord); ./ / comment 2
            return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, options, inTask, outActivity); }Copy the code

In comment 1, the system encapsulates information about the Activity to be started into an ActivityRecord object. An ActivityRecord is a record of an activity in AMS, meaning that each ActivityRecord represents an activity entity. After that, we call another overloaded startActivity method in comment 2. Let’s look at the source:

	private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {
        int result = START_CANCELED;
        try{ mService.mWindowManager.deferSurfaceLayout(); result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags, doResume, options, inTask, outActivity); }...return result;
    }
Copy the code

You can see that the startActivity method calls the startActivityUnchecked method again:

    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {...// Note 1: If the activity to be started is the same activity currently at the top of the stack
        // Check to see if the activity to be started can only be started once
        final ActivityStack topStack = mSupervisor.mFocusedStack;// Get the current top activity stack
        final ActivityRecord topFocused = topStack.topActivity();// Get the activity at the top of the stack
        final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
        
        // Determine whether a new activity should be started
        // If the activity to be started is the same as the activity at the top of the stack, and the mode of the activity to be started is
        // FLAG_ACTIVITY_SINGLE_TOP does not need to be started
        final booleandontStart = top ! =null && mStartActivity.resultTo == null&& top.realActivity.equals(mStartActivity.realActivity) && top.userId == mStartActivity.userId && top.app ! =null&& top.app.thread ! =null&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) ! =0
                || mLaunchSingleTop || mLaunchSingleTask);
        if (dontStart) {
            
            ...
			
            if(mDoResume) { mSupervisor.resumeFocusedStackTopActivityLocked(); }...returnSTART_DELIVERED_TO_TOP; }...// Note 2: Check whether a new activity stack should be created
        // When the startup mode is set to FLAG_ACTIVITY_NEW_TASK, a new activity stack is created to hold the activity to be started
        if (mStartActivity.resultTo == null && mInTask == null&&! mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) ! =0) {
            newTask = true;
			// Create a new task stack
            result = setTaskFromReuseOrCreateNewTask(
                    taskToAffiliate, preferredLaunchStackId, topStack);
        } else if(mSourceRecord ! =null) {
			// Use the task stack of the activity that initiates the request to hold the activity to be launched
            result = setTaskFromSourceRecord();
        } else if(mInTask ! =null) { result = setTaskFromInTask(); }...if (mDoResume) {
            final ActivityRecord topTaskActivity =
                    mStartActivity.getTask().topRunningActivityLocked();
            if(! mTargetStack.isFocusable() || (topTaskActivity ! =null&& topTaskActivity.mTaskOverlay && mStartActivity ! = topTaskActivity)) { mTargetStack.ensureActivitiesVisibleLocked(null.0, !PRESERVE_WINDOWS);
                mWindowManager.executeAppTransition();
            } else {
                if(mTargetStack.isFocusable() && ! mSupervisor.isFocusedStack(mTargetStack)) { mTargetStack.moveToFront("startActivityUnchecked");
                }
                / / comment 3mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, mOptions); }}else{ mTargetStack.addRecentActivityLocked(mStartActivity); }...return START_SUCCESS;
    }
Copy the code

The startActivityUnchecked method is longer in code, but the logic is not complicated. Let’s start with a few lines in comment 1. This is mainly to deal with the case where the activity to be started starts in SINGLE_TOP mode. In comment 1, we first get the information about the activity currently at the top of the stack, and then determine whether the activity to be started is the same as the activty currently at the top of the stack. If the activity to be started is the same activity as the actiivty currently at the top of the stack, and the activity to be started is set to FLAG_ACTIVITY_SINGLE_TOP, Instead of continuing to restart a new activity instance, the system simply reuses the activity at the top of the current stack, returning START_DELIVERED_TO_TOP, and the following code is no longer executed.

The code in comment 2 is mainly used to determine whether the activity to be started should be placed on a new task stack. As we all know, if the startup mode of an activity is set to NEW_TASK, the activity is placed in a separate task stack. As you can see from the code in Comment 2, if the activity to be started has the FLAG_ACTIVITY_NEW_TASK flag set, a new activity stack is created for that activity.

After the system will determine whether the activity to be started can obtain information such as focal point, and then continue to start the activity by comments of three code, let’s take a look at mSupervisor resumeFocusedStackTopActivityLocked method.

Code path: \frameworks\ Base \services\core\ Java \com\ Android \server\am\ActivityStackSupervisor.java

    boolean resumeFocusedStackTopActivityLocked( ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
        / / comment 1
        if(targetStack ! =null && isFocusedStack(targetStack)) {
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }
        / / comment 2
        final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
        if (r == null|| r.state ! = RESUMED) { mFocusedStack.resumeTopActivityUncheckedLocked(null.null); }...return false;
    }
Copy the code

In notes 1, if targetStack not null and targetStack has focus, then executes targetStack resumeTopActivityUncheckedLocked method. In 2 place for the stack is running in the top of a ActivityRecord, if r is null or the state of the r is not RESUMED, will perform mFocusedStack resumeTopActivityUncheckedLocked method. TargetStack and mFocusedStack ActivityStack types of objects, let’s take a look at ActivityStack resumeTopActivityUncheckedLocked method:

boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {...try {
            mStackSupervisor.inResumeTopActivity = true; result = resumeTopActivityInnerLocked(prev, options); }...return result;
    }
Copy the code

ResumeTopActivityUncheckedLocked method again call resumeTopActivityInnerLocked method:

    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {...if(mResumedActivity ! =null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            / / comment 1
            pausing |= startPausingLocked(userLeaving, false, next, false); }...if(next.app ! =null&& next.app.thread ! =null) {... }else{.../ / comment 2
            mStackSupervisor.startSpecificActivityLocked(next, true.true);
        }

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

In comment 1, if there is an activity currently in the foreground, the startPausingLocked method is called to change the activity currently in the foreground state to pasue.

Under normal circumstances, the code is executed to the 2, call the object mStackSupervisor startSpecificActivityLocked method here to continue the activity starts, the method of source code is as follows:

    void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        
	// Note 1: Get information about the process based on information about the activity to be started
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        r.getStack().setLaunchTime(r);

	// Comment 2: If the process in which the activity is to be started exists
        if(app ! =null&& app.thread ! =null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
				/ / comment 3
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "+ r.intent.getComponent().flattenToShortString(), e); }}/ / comment 4
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true.0."activity", r.intent.getComponent(), false.false.true);
    }
Copy the code

In comment 1, the system first obtains the information of the process in which the activity is started according to the information of the activity. In comment 2, the system determines the obtained app object. If the app is not null and the app thread (ActivityThread) is not empty, RealStartActivityLocked at Comment 3 is called to start the activity further if the process to be started exists, otherwise startProcessLocked at Comment 4 is called to create a new process.

The process of creating a new process is more complex and will be covered in a later article, but we’ll look at the process as it already exists. RealStartActivityLocked source code is as follows:

    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {...try{... app.thread.scheduleLaunchActivity(newIntent(r.intent), r.appToken, System.identityHashCode(r), r.info, mergedConfiguration.getGlobalConfiguration(), mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, ! andResume, mService.isNextTransitionForward(), profilerInfo); . }...return true;
    }
Copy the code

In realStartActivityLocked approach directly invoke the app. The thread. ScheduleLaunchActivity to start the Activity. App is to start the activity in the process, the thread that is the process of ActivityThread, this is a Binder object, therefore, app. Thread. ScheduleLaunchActivity is actually across processes call, What is actually called is the scheduleLaunchActivity method in the ActivityThread object of the process in which the activity is being started. At this point, the activity startup process is transferred from AMS to the target application process.

conclusion

Let’s summarize the startup process of this part:

  1. The application initiates a request to AMS to start a new Activity through a Binder mechanism
  2. AMS receives the request and generates or adjusts the relevant ActivityRecord, TaskRecord, and other objects based on the startup mode
  3. If an Activity is already in the foreground, you need to set the Activity to pause
  4. If the application in which you want to start your activity has not already created a process, create a new one
  5. If the application in which the activity is to be started already has a process, notify that process’s ActivityThread to continue the activity

Next: The Activity start process (2) : the application process part