Activity startup process is divided into two kinds, one is that the user clicks on the desktop and Launcher starts App, the other is that App calls startActivity to open a new Activity. The two modes of start-up are basically the same. This paper focuses on the second mode.
The whole Activity startup process can be divided into two parts: App process - > AMS process and AMS process - > App process.
App process - > AMS process
Let's first look at what startActivity() does:
public void startActivity(Intent intent, @Nullable Bundle options) { //Eventually, the startActivityForResult method is called if (options != null) { startActivityForResult(intent, -1, options); } else { startActivityForResult(intent, -1); } }
Click on the startActivityForResult method to see:
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { options = transferSpringboardActivityOptions(options); //ExcStartActivity Method for Executing mInstrumentation Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); } }
mInstrumentation is a private member variable of Activity and an Instrumentation object. Instrumentation encapsulates various methods of interaction between App process and AMS process. mInstrumentation is also a hook point in plug-in activity, which can be replaced by modifying the intent parameter passed into the execStartActivity method.
Look at what mInstrumentation.execStartActivity does:
public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(who); //1. Key code for interacting with AMS processes int result = ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } return null; }
Eliminate irrelevant code, the real core part is in Note 1.
In Note 1, the ActivityManager.getService() method retrieves an IActivityManager object, which can be seen from its name as a binder object. This binder object can take AMS's local proxy object and then call AMS's startActivity method through this proxy object. Let's look at the implementation of the ActivityManager.getService() method:
//note 1 public static IActivityManager getService() { return IActivityManagerSingleton.get(); } //Note 2 private static final Singleton<IActivityManager> IActivityManagerSingleton = //Creating Singleton Objects new Singleton<IActivityManager>() { //Call the create() method of the Singleton object, which returns in the IActivityManager Singleton //Back is an IActivityManager object @Override protected IActivityManager create() { final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE); final IActivityManager am = IActivityManager.Stub.asInterface(b); return am; } }; //Note 3 public abstract class Singleton<T> { private T mInstance; protected abstract T create(); public final T get() { synchronized (this) { if (mInstance == null) { mInstance = create(); } return mInstance; } } }
Note 1 shows that getService() eventually calls the IActivityManagerSingleton.get() method. Note 2 shows that IActivityManagerSingleton is a Singleton object. Singleton is an abstract class. You can see from Note 3 that when we call the get method of the Singleton object, we eventually call the create() method. Note 2 shows that the creation () method of IActivityManagerSingleton eventually returns the IActivityManager object.
So far, the call chain initiated by Activity has entered AMS, which will manage Activity.
AMS process - > App process
What did AMS do when the code went to AMS? Let's first look at AMS's startActivity() method:
//note 1 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()); } //Note 2 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) { enforceNotIsolatedCaller("startActivity"); userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "startActivity", null); return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, null, null, bOptions, false, userId, null, "startActivityAsUser"); }
Eventually, the startActivityMayWait() method of ActivityStarter is invoked
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, TaskRecord inTask, String reason) { //critical code int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason); return res; } }
The key code is the startActivityLocked method. There are a lot of parameters here. We don't need to understand the meaning of each parameter. Let's continue to look at startActivityLocked():
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, TaskRecord inTask, String reason) { //critical code mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord, inTask); return mLastStartActivityResult != START_ABORTED ? mLastStartActivityResult : START_SUCCESS; }
Continue
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, TaskRecord inTask) { int err = ActivityManager.START_SUCCESS; // Pull the optional Ephemeral Installer-only bundle out of the options early. final Bundle verificationBundle = options != null ? options.popAppVerificationBundle() : null; ProcessRecord callerApp = null; if (caller != null) { // Gets the record object of the caller process callerApp = mService.getRecordForAppLocked(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; } } final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0; if (err == ActivityManager.START_SUCCESS) { Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false) + "} from uid " + callingUid); } ActivityRecord sourceRecord = null; ActivityRecord resultRecord = null; if (resultTo != null) { //Get the Activity record where the caller is located sourceRecord = mSupervisor.isInAnyStackLocked(resultTo); if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord); if (sourceRecord != null) { if (requestCode >= 0 && !sourceRecord.finishing) { resultRecord = sourceRecord; } } } final int launchFlags = intent.getFlags(); if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) { //Transfer the Activity execution results from the source Activity to the new Activity being started, and do not enter the branch without returning the results ... } // Eror state judgment check boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp, resultRecord, resultStack, options); //Privilege check ... // Create Activity Record Objects ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid, callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null, mSupervisor, options, sourceRecord); ... final ActivityStack stack = mSupervisor.mFocusedStack; if (voiceSession == null && (stack.mResumedActivity == null || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) { if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, realCallingPid, realCallingUid, "Activity start")) { PendingActivityLaunch pal = new PendingActivityLaunch(r, sourceRecord, startFlags, stack, callerApp); mPendingActivityLaunches.add(pal); ActivityOptions.abort(options); return ActivityManager.START_SWITCHES_CANCELED; } } ... //// Handling pendind Activity startup doPendingActivityLaunchesLocked(false); //critical code return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, options, inTask, outActivity); }
Here we save some information about Activity and process Flags for Intent. Keep looking down:
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(); //critical code result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags, doResume, options, inTask, outActivity); } finally { if (!ActivityManager.isStartResultSuccessful(result) && mStartActivity.getTask() != null) { mStartActivity.getTask().removeActivity(mStartActivity); } mService.mWindowManager.continueSurfaceLayout(); } postStartActivityProcessing(r, result, mSupervisor.getLastStack().mStackId, mSourceRecord, mTargetStack); return result; }
Continue to look at the startActivityUnchecked method:
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, ActivityRecord[] outActivity) { ... // If the activity being started is the same as the activity currently at the top, then we need to check whether it should be started only once. final ActivityStack topStack = mSupervisor.mFocusedStack; final ActivityRecord topFocused = topStack.topActivity(); final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop); final boolean dontStart = 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) { // Make sure that we have correctly restored the top Activity. topStack.mLastPausedActivity = null; if (mDoResume) { //critical code mSupervisor.resumeFocusedStackTopActivityLocked(); } ActivityOptions.abort(mOptions); if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { // We don't need to start a new activity, and the client said not to do // anything if that is the case, so this is it! return START_RETURN_INTENT_TO_CALLER; } deliverNewIntent(top); // Don't use mStartActivity.task to display toast. Instead of starting a new activity, we put it back on top. Fields in mStartActivity may not be fully initialized. mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredLaunchStackId, preferredLaunchDisplayId, topStack.mStackId); return START_DELIVERED_TO_TOP; } boolean newTask = false; final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null) ? mSourceRecord.getTask() : null; // Should this be considered a new task? int result = START_SUCCESS; if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { newTask = true; // Create a new stack result = setTaskFromReuseOrCreateNewTask( taskToAffiliate, preferredLaunchStackId, topStack); } else if (mSourceRecord != null) { result = setTaskFromSourceRecord(); } else if (mInTask != null) { result = setTaskFromInTask(); } else { setTaskToCurrentTopOrCreateNewTask(); } if (result != START_SUCCESS) { return result; } ... if (mDoResume) { final ActivityRecord topTaskActivity = mStartActivity.getTask().topRunningActivityLocked(); if (!mTargetStack.isFocusable() || (topTaskActivity != null && topTaskActivity.mTaskOverlay && mStartActivity != topTaskActivity)) { ... } else { if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) { mTargetStack.moveToFront("startActivityUnchecked"); } mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, mOptions); } } else { mTargetStack.addRecentActivityLocked(mStartActivity); } ... return START_SUCCESS; }
This method mainly deals with Activity stack correlation. Continue to see mSupervisor.resumeFocusedStackTopActivityLocked():
//note 1 boolean resumeFocusedStackTopActivityLocked() { return resumeFocusedStackTopActivityLocked(null, null, null); } //Note 2 boolean resumeFocusedStackTopActivityLocked( ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) { if (!readyToResume()) { return false; } // Target stack has focus if (targetStack != null && isFocusedStack(targetStack)) { //Ensure that top activity in the stack is restored return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); } //Gets the active Activity Record on the top of the stack on which Activation is to be started final ActivityRecord r = mFocusedStack.topRunningActivityLocked(); if (r == null || r.state != RESUMED) { //critical code mFocusedStack.resumeTopActivityUncheckedLocked(null, null); } else if (r.state == RESUMED) { // Kick off any lingering app transitions form the MoveTaskToFront operation. mFocusedStack.executeAppTransition(targetOptions); } return false; }
Keep looking at the key code:
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { boolean result = false; try { mStackSupervisor.inResumeTopActivity = true; //critical code result = resumeTopActivityInnerLocked(prev, options); } finally { mStackSupervisor.inResumeTopActivity = false; } . . . . return result; }
Keep the key code here and click in:
//Note 1: This code is too long. After deleting the unimportant part, there is only one line that you really need to see. private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { ... mStackSupervisor.startSpecificActivityLocked(next, true, true); ... return true; } //Note 2 void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { // Get the process in which Activity was started ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); r.getStack().setLaunchTime(r); // Determine whether the process is running 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); } //critical code realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e); } } mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true); }
Continue to see the realStartActivityLocked() method:
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { ... //critical code app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo); ... return true; }
app.thread is an IApplicationThread type, which is the ApplicationThread proxy object of the App process that AMS obtains through binder. So app.thread.scheduleLaunchActivity finally calls the scheduleLaunchActivity() method of the App process ApplicationThread object. You can see that the call chain eventually returns to the App process.
Start Activity
How do you complete the Activity startup in the App process?
Let's first look at the scheduleLaunchActivity() method of ApplicationThread:
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { updateProcessState(procState, false); ActivityClientRecord r = new ActivityClientRecord(); r.token = token; r.ident = ident; r.intent = intent; r.referrer = referrer; r.voiceInteractor = voiceInteractor; r.activityInfo = info; r.compatInfo = compatInfo; r.state = state; r.persistentState = persistentState; r.pendingResults = pendingResults; r.pendingIntents = pendingNewIntents; r.startsNotResumed = notResumed; r.isForward = isForward; r.profilerInfo = profilerInfo; r.overrideConfig = overrideConfig; updatePendingConfiguration(curConfig); //critical code sendMessage(H.LAUNCH_ACTIVITY, r); }
This method encapsulates some of Activity's information and calls the sendMessage() method.
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 is a member variable of ActivityThread and is a Handler mH.sendMessage(msg); }
MH is a member variable of ActivityThread and a Handler object. mH sends the previously encapsulated Activeness information to the main thread for execution. In the process of plug-in Activity, we usually rewrite a handler to hook off the mH members of the system and complete the plug-in of Activity.
After sending a message through Handler, the handleMessage() method of Handler will eventually be invoked.
private class H extends Handler { public static final int LAUNCH_ACTIVITY = 100; public static final int PAUSE_ACTIVITY = 101; public static final int PAUSE_ACTIVITY_FINISHING= 102; public static final int STOP_ACTIVITY_SHOW = 103; ... public void handleMessage(Message msg) { switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.loadedApk = getLoadedApkNoCheck( r.activityInfo.applicationInfo, r.compatInfo); //critical code handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; ... } Object obj = msg.obj; if (obj instanceof SomeArgs) { ((SomeArgs) obj).recycle(); } } }
As you can see from the previous code, you will eventually move to the branch of LAUNCH_ACTIVITY, and then call the handleLaunchActivity() method
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { . . . . WindowManagerGlobal.initialize(); //critical code Activity a = performLaunchActivity(r, customIntent); if (a != null) { r.createdConfig = new Configuration(mConfiguration); reportSizeConfigurations(r); Bundle oldState = r.state; handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason); if (!r.activity.mFinished && r.startsNotResumed) { if (r.isPreHoneycomb()) { r.state = oldState; } } } else { try { ActivityManager.getService() .finishActivity(r.token, Activity.RESULT_CANCELED, null, Activity.DONT_FINISH_TASK_WITH_ACTIVITY); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } }
The key part is the performance LaunchActivity () method:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // Extract the information to start Activity from Activity Client Record ActivityInfo aInfo = r.activityInfo; if (r.loadedApk == null) { r.loadedApk = getLoadedApk(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); } // Create ContextImpl ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; try { //Get ClassLoader java.lang.ClassLoader cl = appContext.getClassLoader(); // Create Activity through classLoader.loadClass(className).newInstance() 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) { ... } try { // Create Application Objects Application app = r.loadedApk.makeApplication(false, mInstrumentation); ... if (activity != null) { ... // Initialization of data by calling attach() method of Activity, ContextImpl and Activeness associations, Activity and window associations activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback); ... //The key code calls the callActivityOnCreate, which internally calls the performCreate() of Activity to call onCreate(). if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } ... } ... mActivities.put(r.token, r); } catch (SuperNotCalledException e) { ... } catch (Exception e) { ... } return activity; }
Call ActivityOnCreate (), which calls Activity's performCreate() internally to call onCreate().
//note 1 public void callActivityOnCreate(Activity activity, Bundle icicle) { prePerformCreate(activity); activity.performCreate(icicle); postPerformCreate(activity); } //Note 2 final void performCreate(Bundle icicle, PersistableBundle persistentState) { mCanEnterPictureInPicture = true; restoreHasCurrentPermissionRequest(icicle); //Invoke the onCreate() method of Activity so far that Activity has been started if (persistentState != null) { onCreate(icicle, persistentState); } else { onCreate(icicle); } ... }
The code in Note 1 calls the method in Note 2, and finally invokes the onCreate() method of Activity so that Activity has been started.
summary
Activity is initiated by the App process and will eventually return to the App process to complete the start after AMS process processing. Understanding the start-up process of Activity can help us better understand the working principle of Activity and lay the foundation for plug-in of Activity.