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