Preface
Activity can be started in two ways, one is the start process of root activity, the other is the start process of normal activity. The first one refers to the process that an app starts. A common activity refers to the process that calls the startActivity in an application.
Nagan Activity is a more comprehensive startup, but also a good understanding of the entire Android startup process. There are three parts to Launcher request process, AMS to ApplicationThread call process and ActivityThread start activity.
After reviewing the startup process of Android 7, 8, 9, we will now analyze it again based on the source code of android 11. To select the latest to analyze, the design ideas are all clear.
Launcher Start Process
When the app icon is clicked, the launcher's startActivitySafely method is invoked, and the startActivity method is invoked internally.
## Launcher.java public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { ... //Set Start in New Task Stack intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (v != null) { intent.setSourceBounds(getViewBounds(v)); } try { if (Utilities.ATLEAST_MARSHMALLOW && (item instanceof ShortcutInfo) && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) && !((ShortcutInfo) item).isPromise()) { startShortcutIntentSafely(intent, optsBundle, item); } else if (user == null || user.equals(Process.myUserHandle())) { //Call startActivity to start the page startActivity(intent, optsBundle);//2 } else { LauncherAppsCompat.getInstance(this).startActivityForProfile( intent.getComponent(), user, intent.getSourceBounds(), optsBundle); } return true; } catch (ActivityNotFoundException|SecurityException e) { Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e); } return false; }
This sets the intent's Flag to Intent.FLAG_ACTIVITY_NEW_TASK opens a new task stack, and the startActivity method of the activity is called again.
### Activity public void startActivity(Intent intent, @Nullable Bundle options) { ...... if (options != null) { startActivityForResult(intent, -1, options); } else { //We want to make this call compatible with applications that may have overwritten this method. startActivityForResult(intent, -1); } }
Will go to the startActivityForResult method, the second parameter is -1, meaning Launcher does not need to know the result of Activity startup.
### Activitu.java public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); ...... cancelInputsAndStartExitTransition(options); } else { ...... } }
Since this is the first time mParent = null has been created, the Instrumentation's execStartActivity method is called internally. Instrumentation has a method for tracking application s and life cycles.
### Instrumentation public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { ...... try { intent.migrateExtraStreamToClipData(who); intent.prepareToLeaveProcess(who); int result = ActivityTaskManager.getService().startActivity(whoThread, who.getBasePackageName(), who.getAttributionTag(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } return null; }
(New method for ActivityTaskManager and RRoD 10.0, getService is new after android 8.0)
First the ActivityTaskManager.getService() is called to get the proxy object of AMS, then the startActivity method is called, how can I get the proxy object of AMS?
### ActivityTaskManager public static IActivityTaskManager getService() { return IActivityTaskManagerSingleton.get(); } private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton = new Singleton<IActivityTaskManager>() { @Override protected IActivityTaskManager create() { //Get ACTIVITY_ TASK_ Reference to SERVICE, also known as ATMS of type IBinder final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE); //To achieve interprocess communication, the server, or AMS, simply inherits the IActivityManager.Stub class and implements the corresponding methods. return IActivityTaskManager.Stub.asInterface(b); } };
You can see here that a cross-process service, **ActivityTaskManagerService**, inherits from IActivityTaskManager.Stub and is a Binder object. Once you know what this is, you can go back to Instrumentation's execStartActivity method and look down.
ATMS to ApplicationThread process
Next, we enter the service ATMS provided by the system, followed by the call process from ATMS to ApplicationThread, and the diagram shows the sequence:
Continuing with the analysis, you have reached the ATMS startActivity method.
### ActivityTaskManagerService public final int startActivity(IApplicationThread caller, String callingPackage, String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) { //You can see that the startActivityAsUser method has one more parameter, UserHandle.getCallingUserId(), than the startActivity method, which gets the caller's UserId //AMS determines the caller's permissions based on this UserId. return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, UserHandle.getCallingUserId()); } public int startActivityAsUser(IApplicationThread caller, String callingPackage, String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) { return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId, true /*validateIncomingUser*/); } private int startActivityAsUser(IApplicationThread caller, String callingPackage, @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) { assertPackageMatchesCallingUid(callingPackage); //Determine if caller process is isolated enforceNotIsolatedCaller("startActivityAsUser"); //Check caller permissions userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser, Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser"); // Get the ActivityStart object and start the activity at the last excute. return getActivityStartController().obtainStarter(intent, "startActivityAsUser") .setCaller(caller) .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) .setResolvedType(resolvedType) .setResultTo(resultTo) .setResultWho(resultWho) .setRequestCode(requestCode) .setStartFlags(startFlags) .setProfilerInfo(profilerInfo) .setActivityOptions(bOptions) .setUserId(userId) .execute(); }
After invoking layer by layer, we come to the end, get the ActivityStart object through etActivityStartController().obtainStarter, and start the activity in the last excute method.
### ActivityStarter //Execute Request to Start Activity int execute() { try { ...... if (mRequest.activityInfo == null) { mRequest.resolveActivity(mSupervisor); } int res; synchronized (mService.mGlobalLock) { final boolean globalConfigWillChange = mRequest.globalConfig != null && mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0; final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack(); ...... final long origId = Binder.clearCallingIdentity(); res = resolveToHeavyWeightSwitcherIfNeeded(); if (res != START_SUCCESS) { return res; } //Perform Start Interface Request res = executeRequest(mRequest); Binder.restoreCallingIdentity(origId); ...... return getExternalResult(mRequest.waitResult == null ? res : waitForResult(res, mLastStartActivityRecord)); } } finally { onExecutionComplete(); } }
(executeRequest is a new method for Android11)
Look at the executeRequest method again, and focus on the main methods:
### ActivityStarter.java //Perform the activity start request to start the activity start journey. First, several preliminary checks are performed. The normal activity start-up process passes through private int executeRequest(Request request) { ..... mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession, request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask, restrictedBgActivity, intentGrants); if (request.outActivity != null) { request.outActivity[0] = mLastStartActivityRecord; } return mLastStartActivityResult; }
It also calls the startActivityUnchecked method.
### ActivityStarter //Primarily deals with stack management related logic //Start an activity after preliminary checking and confirming that the caller has the required permissions to perform this operation private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, Task inTask, boolean restrictedBgActivity, NeededUriGrants intentGrants) { int result = START_CANCELED; final ActivityStack startedActivityStack; try { mService.deferWindowLayout(); Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner"); result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor, startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); startedActivityStack = handleStartResult(r, result); mService.continueWindowLayout(); } postStartActivityProcessing(r, result, startedActivityStack); return result; }
startActivityUnchecked deals primarily with stack management-related logic. The startActivityInner method is then called, and finally the resumeFocusedStacksTopActivities method of RootWindowContainer is called.
### ActivityStarter //Start an activity and determine if it should be added to the top of an existing task or deliver a new intent to an existing activity. Active tasks are also operated on to the requested or valid stack display. int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, Task inTask, boolean restrictedBgActivity, NeededUriGrants intentGrants) { ...... if (newTask) { final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null) ? mSourceRecord.getTask() : null; //A new Activity Task Stack is created internally setNewTask(taskToAffiliate); if (mService.getLockTaskController().isLockTaskModeViolation( mStartActivity.getTask())) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } } else if (mAddingToTask) { addOrReparentStartingActivity(targetTask, "adding to task"); } ...... //Focus primarily on this line of code mRootWindowContainer.resumeFocusedStacksTopActivities( mTargetStack, mStartActivity, mOptions); 1 ...... return START_SUCCESS; }
RootWindowContainer is a new class for Android 10
RootWindowContainer shared some of the functionality of the previous ActivityStackSupervisor. Inside resumeFocusedStacksTopActivities, the resumeTopActivityUncheckedLocked method of ActivityStack is called again, and then we'll go in and look at it.
### ActivityStack boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { if (mInResumeTopActivity) { // Do not process if top Activity is executing return false; } boolean result = false; try { // Protect against recursion. mInResumeTopActivity = true; result = resumeTopActivityInnerLocked(prev, options); //When top Activity is restored, it may be necessary to pause top Activity final ActivityRecord next = topRunningActivity(true /* focusableOnly */); if (next == null || !next.canTurnScreenOn()) { checkReadyForSleep(); } } finally { mInResumeTopActivity = false; } return result; }
Next, skip to the resumeTopActivityInnerLocked method to see:
### ActivityStack private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { ...... if (mResumedActivity != null) { if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Pausing " + mResumedActivity); //When the previous interface is active, the previous interface is paused pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next); } ....... final ClientTransaction transaction = ClientTransaction.obtain(next.app.getThread(), next.appToken); ....... //The ResumeActivityItem transaction is sent to the client when the Activity is started transaction.setLifecycleStateRequest( ResumeActivityItem.obtain(next.app.getReportedProcState(), dc.isNextTransitionForward())); mAtmService.getLifecycleManager().scheduleTransaction(transaction); ...... if (!next.hasBeenLaunched) { next.hasBeenLaunched = true; } else { if (SHOW_APP_STARTING_PREVIEW) { //Cold Start Display White Screen next.showStartingWindow(null /* prev */, false /* newTask */, false /* taskSwich */); } if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next); //Continue with current activity, normal start of normal activity mStackSupervisor.startSpecificActivity(next, true, true); } ...... return true; }
(There are more than 300 lines of this method!!) After pausing the previous activity and making a series of judgments on the previous interface, next.showStarting Window is called before starting the activity to show a window, then mStackSupervisor.startSpecific Activity is called to start the normal startup.
### ActivityStackSupervisor void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) { final WindowProcessController wpc = mService.getProcessController(r.processName, r.info.applicationInfo.uid); boolean knownToBeDead = false; //Is the application process started if (wpc != null && wpc.hasThread()) { try { //Finally, you see the real way to start activity realStartActivityLocked(r, wpc, andResume, checkConfig); return; } catch (RemoteException e) { Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e); } knownToBeDead = true; } r.notifyUnknownVisibilityLaunchedForKeyguardTransition(); final boolean isTop = andResume && r.isTopRunningActivity(); //todo analysis mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity"); }
We can't wait to see the realStartActivityLocked method in a moment.
### ActivityStackSupervisor.java boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc, boolean andResume, boolean checkConfig) throws RemoteException { ...... final Task task = r.getTask(); final ActivityStack stack = task.getStack(); beginDeferResume(); try { ...... final MergedConfiguration mergedConfiguration = new MergedConfiguration( proc.getConfiguration(), r.getMergedOverrideConfiguration()); r.setLastReportedConfiguration(mergedConfiguration); logIfTransactionTooLarge(r.intent, r.getSavedState()); // Create Activity Start Transaction //proc.getThread, also known as IApplicationThread, is the proxy of ApplicationThread mentioned earlier in the system process. final ClientTransaction clientTransaction = ClientTransaction.obtain( proc.getThread(), r.appToken); final DisplayContent dc = r.getDisplay().mDisplayContent; clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, mergedConfiguration.getGlobalConfiguration(), mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(), results, newIntents, dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(), r.assistToken, r.createFixedRotationAdjustmentsIfNeeded())); // Set the desired final state final ActivityLifecycleItem lifecycleItem; if (andResume) { lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward()); } else { lifecycleItem = PauseActivityItem.obtain(); } //The life cycle state in which a client should be in after performing a transaction. clientTransaction.setLifecycleStateRequest(lifecycleItem); // Finally, IAppliicatioinThread implements cross-process mService.getLifecycleManager().scheduleTransaction(clientTransaction); ...... return true; }
Well, when we analyze it, ClientTransaction can see that it contains a series of messages that can be sent to the client. This includes callback lists and life cycle states. ClientTransaction.obtain (proc.getThread(), r.appToken), where proc.getThread is the IApplicationThread, which is the proxy of ApplicationThread mentioned earlier in the system process.
Next, clientTransaction's addCallback method passes in LaunchActivityItem, so let's see exactly what LaunchActivityItem.obtain is adjusting?
### LaunchActivityItem //Gets an instance initialized with the supplied parameters. public static LaunchActivityItem obtain(Intent intent, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments) { LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class); if (instance == null) { instance = new LaunchActivityItem(); } setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer, voiceInteractor, procState, state, persistentState, pendingResults, pendingNewIntents, isForward, profilerInfo, assistToken, fixedRotationAdjustments); return instance; }
It can be a variety of setup operations before starting. How did you successfully launch the Activity in the end?
Look at the following method: mService.getLifecycleManager(). scheduleTransaction, where mService is also the ActivityTaskManagerService, calls the getLifeCycleManager method, gets ClientLifecycleManager, and calls the scheduleTransaction inside.
### ClientLifecycleManager void scheduleTransaction(ClientTransaction transaction) throws RemoteException { final IApplicationThread client = transaction.getClient(); transaction.schedule(); if (!(client instanceof Binder)) { transaction.recycle(); } }
Let's see what transaction.schedule() did again.
### ClientTransaction public void schedule() throws RemoteException { //It will be sent to the client mClient.scheduleTransaction(this); }
Seeing that IApplicationThread's scheduleTransaction method is invoked here, we also know that the startup operation crosses the process again to the client. Because IApplicationThread is the proxy of ApplicationThread in the system process, that is, where it actually executes is in the client's ApplicationThread. In other words, Application is the communication bridge between ATMS processes and application processes.
epilogue
Here we also learn Launcher's request for ATMS and how ATMS handles launching to ApplicationThread. A part of the Activity Thread launching Activity is analyzed in the next section.
Reference resources
Exploration of the Art of Android Development
Android goes deep into four components (6) Android 8.0 Root Activity Launch Process (Previous)
Android goes deep into four components (7) Android 8.0 Root Activity Launch Process (later)
Activity Startup Process Detailed (Based on Android 10.0)
In-process Activity Startup Process Source Research, based on android 9.0