preface
- Starting an Activity on Android8.0
The last article described how an application process requests AMS to start an Activity and how an Activity is started in AMS. The code logic for starting an Activity is then returned from the AMS process to the ApplicationThread. We are left with the question of how the Activity’s lifecycle methods are called back. ApplicationThread starts an Activity in the application process.
This article is based on Android8.0, the relevant source file location of this article is as follows: frameworks/base/core/java/android/app/Activity.java frameworks/base/core/java/android/app/ActivityThread.java frameworks/base/core/java/android/app/Instrumentation.javaCopy the code
ApplicationThread::scheduleLaunchActivity()
The scheduleLaunchActivity method in ApplicationThread is called in ActivityStackSupervisor’s realStartActivityLocked(). This is the start of the Activity. ApplicationThread is an internal class of ActivityThread that implements the iApplicationThread.stub interface. ActivityThread represents the main thread of the application process, which manages the threads of the current application process.
ScheduleLaunchActivity scheduleLaunchActivity
//ActivityThread::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) {
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);
sendMessage(H.LAUNCH_ACTIVITY, r);
}
Copy the code
The above method simply encapsulates the parameters passed from AMS to start the Activity into ActivityClientRecord, and then calls sendMessage to send H the LAUNCH_ACTIVITY message. H is an internal class in the ActivityThread. H is a Handler type. All Activity startup messages are handled by this Handler. Because it is still running in the Binder thread pool, the Activity cannot be started, so you have to switch to the main thread for the Activity’s lifecycle method callbacks.
Let’s look at the sendMessage method.
1, ApplicationThread: : sendMessage ()
The method is as follows:
//ActivityThread::ApplicationThread
private void sendMessage(int what, Object obj) {
sendMessage(what, obj, 0.0.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);
}
Copy the code
As you can see, the sendMessage method wraps H.launch_activity and ActivityClientRecord into a Message, and then calls the sendMessage method mH, which is an instance of H, as follows:
final H mH = new H();
Copy the code
If you’re familiar with android messaging, you know that when a Handler sends a message, it puts it into the handlerMessage method.
Let’s take a look at what Handler H does to the message.
2, H::handleMessage ()
As follows:
//ActivityThread.java
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int RESUME_ACTIVITY = 107;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
/ /...
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null."LAUNCH_ACTIVITY");
}
break;
/ /...}}}Copy the code
The LAUNCH_ACTIVITY field is used as the LAUNCH_ACTIVITY field. The obJ field in MSG is converted to ActivityClientRecord. Then assign the ActivityClientRecord packageInfo, which is of type LoadedApk and represents the LoadedApk file, Next, the handleLaunchActivity method of the external class ActivityThread is called.
Next, let’s look at the handleLaunchActivity method for ActivityThread.
ActivityThread: : handleLaunchActivity ()
The source code for this method is as follows:
//ActivityThread.java
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
/ /...
// The final callback to the Activity's onConfigurationChanged method
handleConfigurationChanged(null.null);
// This is the local proxy to get the WindowManager system service
WindowManagerGlobal.initialize();
// Start the Activity by calling ActivityThread's performLaunchActivity method, which will eventually call back the Activity's onCreate and onStart methods
Activity a = performLaunchActivity(r, customIntent);
if(a ! =null) {
/ /...
Calling the ActivityThread's handleResumeActivity method will eventually call back the Activity's onResume method
handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed, r.lastProcessedSeq, reason);/ /...
}else {
// If something goes wrong, AMS is told to stop starting the Activity
try {
ActivityManager.getService()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throwex.rethrowFromSystemServer(); }}}Copy the code
This article focuses on how the Activity lifecycle is called back, so we’ll just focus on comments 1 and 2 above. Note 1 calls the performLaunchActivity method of ActivityThread. This method completes the creation and startup of the Activity object and notifies AMS to stop starting the Activity if something goes wrong. And in comment 2, the ActivityThread resumes the Activity that is started via handleResumeActivity.
Let’s start with the performLaunchActivity method in comment 1.
1, ActivityThread: : performLaunchActivity ()
The source code for this method is as follows:
//ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// Get ActivityInfo from ActivityClientRecord.
ActivityInfo aInfo = r.activityInfo;
PackageInfo is the LoadedApk type described earlier
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
/ / get the ComponentName
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 the context in which to start the Activity
ContextImpl appContext = createBaseContextForActivity(r);
// Construct the Activity object and set the parameters
Activity activity = null;
try {
// Get the class loader
java.lang.ClassLoader cl = appContext.getClassLoader();
// Create an instance of the Activity with the Instrumentation class loading
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
// Set parameters to start the Activity
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if(r.state ! =null) { r.state.setClassLoader(cl); }}catch (Exception e) {
// Failed to initialize the Activity
//....
}
try {
/ / create the Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
/ /...
if(activity ! =null) {
// Construct the Configuration object
Configuration config = new Configuration(mCompatConfiguration);
/ /...
// Associate the Activity with ContextImpl
appContext.setOuterContext(activity);
// Attach the information created above to the Activity to initialize the Activity, such as ContextImpl, Application, Configuration
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);
/ /...
// Get the Activity theme and set it
int theme = r.activityInfo.getThemeResource();
if(theme ! =0) {
activity.setTheme(theme);
}
activity.mCalled = false;
Call the callActivityOnCreate method in Instrumentation to notify the Activity that it has been created, which will eventually call the Activity's onCreate method, depending on whether persistence is required
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
// Focus here, go to this branch
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if(! activity.mCalled) {/ /...
// The Activity's onCreate method cannot be called, raising an exception
}
r.activity = activity;
r.stopped = true;
if(! r.activity.mFinished) {//2, the Activity's onStart method is eventually called
activity.performStart();
r.stopped = false;
}
if(! r.activity.mFinished) {/ / according to whether need persistence, call the Instrumentation callActivityOnRestoreInstanceState method notice the Activity has been created, This will eventually call the Activity's onRestoreInstanceState method
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); }}/ /...
}
r.paused = true;
// Cache the ActivityClientRecord for later use. ArrayMap
mActivities = new ArrayMap<>();
,>
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
/ /...
} catch (Exception e) {
/ /...
// An exception is thrown and the Activity cannot be started
}
return activity;
}
Copy the code
The main code for this method is posted and commented out, and it mainly does the following:
- 1. Obtain the component information of the Activity to be started from ActivityClientRecord, such as ActivityInfo and ComponentName.
The ActivityInfo class is used to store the activity node information set by AndroidManifes. The ComponentName class is used to store the package name and class name of the Activity
- 2, through createBaseContextForActivity method to create an environment to start the Activity context ContextImp, and in the method as a parameter to pass into the attach below.
ContextImp is the implementation of the Context. Most of the logic in the Context is given to the ContextImpl. Context defines many methods that are closely related to the four major component startup, system-level service acquisition, class loading, resource acquisition, etc. The Activity inherits from ContextThemeWrapper, ContextThemeWrapper inherits from ContextWrapper, ContextWrapper inherits from Context, ContextWrapper contains a mBase reference to the Context, and within the attach method of the Activity, attachBaseContext is called to assign the ContextImp to the mBase. So the Activity is a wrapper class for ContextImpl, and the Activity extends the methods in the Context. (Here is a decorator pattern)
- 3. Create the Application with the makeApplication method of LoadedApk.
In the makeApplication method, the class loader is used to create an Application using the newApplication method of Instrumentation. If the Application has already been created, it will not be created again. If it is created successfully, The onCreate method of Application is followed by the callApplicationOnCreate of Instrumentation.
- 4, through the newActivity method of Instrumentation with the class loader to create an Activity object.
Instrumentation is a class used to monitor the interaction between applications and the system, through which you can create instances of activities, Applicationd, and Activity lifecycle callbacks. So in below see mInstrumentation callActivityOnXX, are generally to some Activity lifecycle callback methods.
- 5. Initialize some important data by attaching method of the Activity, such as ContextImpl, Application, Configuration, etc.
In the Attach method, a Window object (PhoneWindow) is created and associated with the Activity itself, so that when the Window receives an external input event, it can pass the event to the Activity. As mentioned in point 2, the ContextImpl is also associated with the Activity.
- 6, call the Instrumentation callActivityOnCreate method, which will eventually call the Activity’s onCreate method.
So this is comment 1 that’s the focus, and it also says that it calls the callActivityOnCreate method of mInstrumentation with different parameters, depending on whether or not persistence is required. What is this persistence? PersistableMode is a persistableMode property added to the Activity after API 21. Set it to Android :persistableMode= “persistAcrossReboots” in the Activity node of androidmanifest.xml. The data is stored in outPersistentState (Bundle type) and can be recovered even after shutdown. The PersistableMode enables Activity data to be persisted, which is not the focus of this article.
So we usually don’t use this property, will go into the else branch, call mInstrumentation. CallActivityOnCreate (activity, r.s Tate) method.
And Instrumentation: 1.1: callActivityOnCreate ()
The source code for this method is as follows:
//Instrumentation.java
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
// call the Activity's performCreate method
activity.performCreate(icicle);
postPerformCreate(activity);
}
Copy the code
Look at comment 1, which calls the activity’s performCreate method.
1.1.1, Activity: : performCreate ()
The source code for this method is as follows:
//Activity.java
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
restoreHasCurrentPermissionRequest(icicle);
//1, see our hero! The onCreate method
onCreate(icicle, persistentState);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
Copy the code
Note 1 calls the familiar onCreate method that initializes the Activity’s controls, resources, and so on.
Let’s go back to the performLaunchActivity method of ActivityThread, following point 6 above.
- 7. Call the Activity’s performStart method, which eventually calls the Activity’s onStart method.
So this is comment 2, which I’m focusing on, so let’s look at the performStart method.
And the Activity: 1.2: performStart ()
Let’s go ahead and check it out:
//Activity.java
final void performStart(a) {
/ /...
mCalled = false;
Call Instrumentation callActivityOnStart
mInstrumentation.callActivityOnStart(this);
if(! mCalled) {/ /...
// The Activity's onStart method cannot be called, raising an exception
}
/ /...
}
Copy the code
See note 1, this method is still the same routines, call mInstrumentation. CallActivityOnStart approach, we look at the Instrumentation callActivityOnStart method:
1.2.1, Instrumentation: : callActivityOnStart ()
The method is as follows:
//Instrumentation.java
public void callActivityOnStart(Activity activity) {
// Meet our hero! OnStart method
activity.onStart();
}
Copy the code
A simple line of code that calls the familiar onStart method.
Continue to go back to our ActivityThread performLaunchActivity method, also have not finished analysis, next to the call of Instrumentation callActivityOnRestoreInstanceState method according to need, It will eventually call the Activity’s onRestoreInstanceState method. The purpose of this method is not the focus of this article, but we can conclude that the onRestoreInstanceState method is called after the onStart method. Finally, ActivityThread caches the ActivityClientRecord.
After analyzing this long method, in fact, the article is related to the sixth and seventh points. We jump out ActivityThread: : performLaunchActivity method, back to ActivityThread handleLaunchActivity method. Now that our Activity has called back to the onCreate and onStart methods, the onResume method should follow.
Next we look at the handleResumeActivity method in comment 2 of the handleLaunchActivity method.
2, ActivityThread: : handleResumeActivity ()
The source code for this method is as follows:
//ActivityThread.java
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
// Pull the ActivityClientRecord from the cache
ActivityClientRecord r = mActivities.get(token);
/ /...
The performResumeActivity method is called, which eventually calls the Activity's onResume method
r = performResumeActivity(token, clearHide, reason);
// if (r! = null) {} all the logic in the branch is to display the Activity
if(r ! =null) {
/ / get the Activity
final Activity a = r.activity;
/ /...
booleanwillBeVisible = ! a.mStartedActivity;if(! willBeVisible) {try {
willBeVisible = ActivityManager.getService().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throwe.rethrowFromSystemServer(); }}if (r.window == null && !a.mFinished && willBeVisible) {
// Get the Window associated with the Activity
r.window = r.activity.getWindow();
// Get the Activity's DecorView, which is the Activity's top-level View
View decor = r.window.getDecorView();
// Make the DecorView invisible
decor.setVisibility(View.INVISIBLE);
// Get the ViewManager to add the DecorView
ViewManager wm = a.getWindowManager();
// Get layout participation
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
// Set the layout parameters
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
/ /...
if (a.mVisibleFromClient) {
if(! a.mWindowAdded) { a.mWindowAdded =true;
// Add a DecorView with ViewManager
wm.addView(decor, l);
}
/ /...}}/ /...
// At this point, the Window used to host the DecorView has been added by WM, but is still INVISIBLE, so let's set it to VISIBLE
if(! r.activity.mFinished && willBeVisible && r.activity.mDecor ! =null && !r.hideForNow) {
/ /...
if (r.activity.mVisibleFromClient) {
// Display the Activityr.activity.makeVisible(); }}}else {
/ /...
// The Activity cannot Resume, notifying AMS to terminate the Activity}}Copy the code
The code in the handleResumeActivity method is also a bit long. This method basically sets the Activity to Resume and displays it, so we’ll just focus on comment 1. Remember that an Activity is also a View, and its top View is called a DecorView. However, when the system calls back to the Activity’s onResume function, it simply means that Activity1 has completed all of its resource preparation. The Activity is ready to be displayed to the user, so it should be displayed in a way similar to setVisible. This process involves WindowManage knowledge. Why do this? You can see the relationship between Windows,WindowManager, and WindowManagerService in this article, so note 2 is not the focus of this article.
Let’s look at the performResumeActivity method of comment 1.
ActivityThread: 2.1: performResumeActivity ()
The main source of this method is as follows:
//ActivityThread.java
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide, String reason) {
// Retrieve the ActivityClientRecord from the cache
ActivityClientRecord r = mActivities.get(token);
if(r ! =null && !r.activity.mFinished) {
if (clearHide) {
r.hideForNow = false;
r.activity.mStartedActivity = false;
}
/ /...
try {
/ /...
// Call the Activity's performResume method
r.activity.performResume();
/ /...
r.paused = false;
r.stopped = false;
r.state = null;
r.persistentState = null;
}catch(Exception e) {
/ /...
// Resume Activity cannot resume}}return r;
}
Copy the code
Focus on comment 1, which calls the Activity’s performResume, similar to performCreate and performStart above.
Let’s look at the Activity’s performResume method.
2.1.1 the Activity: : performResume ()
The main source of this method is as follows:
//Activity.java
final void performResume(a) {
// This handles the Restart process in the Activity lifecycle
performRestart();
/ /...
mCalled = false;
Call callActivityOnResume in Instrumentation
mInstrumentation.callActivityOnResume(this);
if(! mCalled) {/ /...
The Activity's onResume method cannot be called because an exception is thrown}}Copy the code
OnReStart () -> onStart(), onResume(), onResume() So we can see the callActivityOnResume method in annotation 1, which is similar to callActivityOnCreate() and callActivityOnStart () above.
In the spirit of persistent attitude, we still look at the Instrumentation callActivityOnResume method.
2.1.2, Instrumentation: : callActivityOnResume ()
//Instrumentation.java
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
// Return to the onResume method
activity.onResume();
/ /...
}
Copy the code
This method assigns the Activity’s mResumed value to true and then calls back to the familiar onResume method.
We jump out of the ActivityThread’s handleResumeActivity method and go back to the handleLaunchActivity method. Now that the handleLaunchActivity method is analyzed, the Activity is displayed to the user.
conclusion
So far we have called back three Activity lifecycle methods: onCreate -> onStart -> onResume. OnRestart is also introduced.
So now we know how ApplicationThread starts the Activity in the application process.
OnPause -> onStop -> onDestory As we know, there are seven life cycle methods for an Activity. Except for onRestart, the other three methods are corresponding to each other.
-
1. AMS contains a local proxy for ApplicatiThread, so AMS processes can use this proxy to communicate with the main thread of ActivityThread and call ApplicatiThread methods.
-
2. The application process also contains a local proxy object for AMS, so the application process can communicate with AMS through this proxy and request AMS to start an Activity.
-
3. Both parties contain agents of both parties, with Binder, communication channels are established for both parties.
Each application has its own Activity task stack, which is managed by AMS. In the case of this article, an Activity has been started and added to the top of the stack. If I press the back button to return to the previous Activity, The Activity calls the corresponding callback onPause -> onStop -> onDestory, which corresponds to an action on the AMS side, AMS calls schedulePauseActivity, scheduleStopActivity, and scheduleDes in ApplicationThread just as the scheduleLaunchActivity method is called when the Activity is started The troyActivity method is used to terminate the Activity. The call process is IPC, so you can see how the rest of the Activity lifecycle is called back. This process is dependent on the interaction with AMS.
We have now gone through the process that occurs after startActivity. In the whole process, I also found a lot of knowledge points left behind in my daily life, which enabled me to further understand Activity. I hope you found that interesting.
References:
How is an Activity lifecycle callback called back
Android source code Analysis and Combat