This is the 8th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
(Android11.0) In-depth mining of App startup process (Part 1)
As mentioned earlier, the Activity starts in a full circle in the ATMS and is eventually returned to the ApplicationThread by calling ClientTransaction’s Schedule method. So let’s move on to the boot process.
ActivityThread start-up Activity
Let’s look at the ApplicadtionThread scheduleTransaction method:
### ActivityThread/ApplicationThread
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
ActivityThread.this.scheduleTransaction(transaction);
}
Copy the code
The final implementation actually calls the ActivityThread’s parent class, ClientTransactionHandler:
# # #ClientTransactionHandler
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
Copy the code
The method is as simple as sending a startup message to a Handler with a simple name: H. This is where the sendMessage method is called to send a message of type EXECUTE_TRANSACTION to class H, as shown below:
# # #ActivityThread
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);
}
Copy the code
MH refers to H, which is the inner class of ActivityThread and inherits Handler, the message management class for the main thread in the application process.
Where is this message coming from? The server’s Binder methods run in Binder’s thread pool, which means that the scheduleTransaction of the system’s cross-process calls to ApplicationThread are executed in Binder’s thread pool.
Handler H handles the message as follows:
### ActivityThread
final H mH = new H();
class H extends Handler {
public static final int BIND_APPLICATION = 110; .public static final int EXECUTE_TRANSACTION = 159;
public static final int RELAUNCH_ACTIVITY = 160; .public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case EXIT_APPLICATION:
if(mInitialApplication ! =null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break; .case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
// Client transactions within the system process are recycled on the client side rather than the ClientLifecycleManager to avoid being cleared before the message is processed.
transaction.recycle();
}
// Reclaim the local scheduled transaction.
break;
case RELAUNCH_ACTIVITY:
handleRelaunchActivityLocally((IBinder) msg.obj);
break; . }... }}Copy the code
Look at the processing of EXECUTE_TRANSACTION in H’s handleMessage method. Take out the ClentTransaction instance and call the execute method.
# # #TransactionExector
public void execute(ClientTransaction transaction) {
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");
finalIBinder token = transaction.getActivityToken(); . executeCallbacks(transaction); executeLifecycleState(transaction); mPendingActions.clear();if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}
Copy the code
Then look at the executeCallbacks method:
# # #TransactionExector
public void executeCallbacks(ClientTransaction transaction) {
finalList<ClientTransactionItem> callbacks = transaction.getCallbacks(); .final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
final int postExecutionState = item.getPostExecutionState();
final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
item.getPostExecutionState());
if(closestPreExecutionState ! = UNDEFINED) { cycleToPath(r, closestPreExecutionState, transaction); }// Key code
item.execute(mTransactionHandler, token, mPendingActions);
item.postExecute(mTransactionHandler, token, mPendingActions);
if (r == null) {
// Launch activity request will create an activity record.r = mTransactionHandler.getActivityClient(token); }... }}Copy the code
You can see that the Callbacks are iterated through and the execute method is called. So what’s the item here?
### ActivityStackSupervisor.realStartActivityLocked
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
r.assistToken, r.createFixedRotationAdjustmentsIfNeeded()));
Copy the code
As we know from the previous article, the LaunchActivityItem subclass of ClientTransactionItem is actually put into the callbacks, so let’s focus on the Execute method in LaunchActivityItem.
# # #launchActivityItem
public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
Copy the code
Here ClientTransactionHandler is the parent of ActivityThread. Encapsulate the parameters that start the Activity as ActivityClientRecord, and finally call the handleLaunchActivity method.
The Activity starts the core implementation
The following start process code basic Android7.0,8.0,9.0,10.0
# # #ActivityThread
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {...// Initialize before creating the activity
if(! ThreadedRenderer.sRendererDisabled && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0) {
HardwareRenderer.preload();
}
WindowManagerGlobal.initialize();
GraphicsEnvironment.hintActivityLaunch();
/ / start the Activity
final Activity a = performLaunchActivity(r, customIntent);
if(a ! =null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
if(! r.activity.mFinished && pendingActions ! =null) {
pendingActions.setOldState(r.state);
pendingActions.setRestoreInstanceState(true);
pendingActions.setCallOnPostCreate(true); }}else {
try {
// Stop the Activity from starting
ActivityTaskManager.getService()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throwex.rethrowFromSystemServer(); }}return a;
}
Copy the code
The performLaunchActivity method completes the creation and launch of the Activity object. Let’s take a look at what the performActivity method does.
### ActivityThread
// Core implementation
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// Retrieve the component information for the Activity to be started from ActivityClientRecord
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
// Get the description class LoadedApk for the APK file
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
//ComponentName holds the package name and class name for the ActivityComponentName component = r.intent.getComponent(); .// Create the context in which to start the Activity
// Most of the logic in Context is done by ContextImpl
ContextImpl appContext = createBaseContextForActivity(r);
// Create an Activity object using class loading through the newActivity method of Instrumentation.
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// Create an instance of the Activity using the class loader based on the Activity class name stored in ComponentName
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 {
// The makeApplication method calls Application's onCreate method inside the makeApplication method
Application app = r.packageInfo.makeApplication(false, mInstrumentation); / / comment 1.if(activity ! =null) {... Window window =null;
if(r.mPendingRemoveWindow ! =null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
// The active resource must be initialized using the same loader as the application context.
appContext.getResources().addLoaders(
app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
appContext.setOuterContext(activity);
// Initialize the Activity
// The attach method creates the PhoneWindow object and associates it with the Activity itself.
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,
r.assistToken);
if(customIntent ! =null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
checkAndBlockForNetworkAccess();
activity.mStartedActivity = false; . activity.mCalled =false;
if (r.isPersistable()) {
Call the callActivityOnCreate method in Instrumentation to start the Activity
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); / / comment 2
} else{ mInstrumentation.callActivityOnCreate(activity, r.state); }... r.activity = activity; mLastReportedWindowingMode.put(activity.getActivityToken(), config.windowConfiguration.getWindowingMode()); } r.setState(ON_CREATE);synchronized(mResourcesManager) { mActivities.put(r.token, r); }}catch (SuperNotCalledException e) {
throwe; }...return activity;
}
Copy the code
Again in comment 1, how is the Application created? The makeApplication method, as follows:
# # #LoadApk
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if(mApplication ! =null) {
returnmApplication; }... Application app =null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application"; }...try {
final java.lang.ClassLoader cl = getClassLoader();
if(! mPackageName.equals("android")) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"initializeJavaContextClassLoader"); initializeJavaContextClassLoader(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); }... ContextImpl appContext = ContextImpl.createAppContext(mActivityThread,this);
NetworkSecurityConfigProvider.handleNewApplication(appContext);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
.......
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
if(instrumentation ! =null) {
try {
// Enable Instrumentation
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if(! instrumentation.onException(app, e)) { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ":" + e.toString(), e);
}
}
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return app;
}
Copy the code
The creation of the Applicatiion object is also accomplished through Instrumentation, which, like the creation of the Actiivty object, is accomplished through the class loader.
Note 2 calls the callActivityOnCreate method in the Instrumentation to start the Activity.
### Instrumenttation
// Call the activity. onCreate method to execute the Activity.
public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}
Copy the code
Finally, the Activity’s performCreate method is called.
# # #Activity
final void performCreate(Bundle icicle, PersistableBundle persistentState) { dispatchActivityPreCreated(icicle); .if(persistentState ! =null) {
onCreate(icicle, persistentState);
} else{ onCreate(icicle); }... dispatchActivityPostCreated(icicle); }Copy the code
Since the Activity’s onCreate method has already been called, this also means that the Activity has completed the entire startup process.
To summarize: performLaunchActivity does the following.
- Get component information for the Activity to be started from ActivityClientRecord
- ContextImpl is created and initialized with the ContextImpl object using the Activity. attach method. This allows the Window to receive external events and pass them to the Activity
- The Application object is created using the makeApplication method of LoadedApk, internally using the mInstrumentation class loader, After creation is called instrumentation. CallApplicationOnCreate method, namely the onCreate method of Application.
- Call the Activity’s onCreate method, is through mInstrumentation callActivityOnCreate method.
When the Activity’s onResume is called
Go around and don’t see where onStart and onResume are called. Did we miss it?
Back to ActivityStackSupervisor’s realStartActivityLocked method:
# # #ActivityStackSupervisor
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {...// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
/ / ResumeActivityItem here
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
} else{ lifecycleItem = PauseActivityItem.obtain(); } clientTransaction.setLifecycleStateRequest(lifecycleItem); . }Copy the code
I was pleasantly surprised to find instances of ResumeActivityItem and PauseActivityItem.
First we need to know what these stand for:
-
LaunchActivityItem onCreate lifecycle transaction on the remote App
-
ResumeActivityItem onResume lifecycle transaction on the remote App
-
PauseActivityItem onPause life cycle transaction on the remote App
-
StopActivityItem onStop lifecycle transaction on the remote App
-
DestroyActivityItem Remote App onDestroy lifecycle transaction
Now let’s focus on ResumeActivityItem
# # #ClientTrasaction
public void setLifecycleStateRequest(ActivityLifecycleItem stateRequest) {
// The final lifecycle state that the client activity should be in after the transaction.
mLifecycleStateRequest = stateRequest;
}
Copy the code
Added to mLifecycleStateRequest, which represents the final lifecycle state that the client activity should be in after the transaction.
So let’s go ahead and see where it’s used? We return the message handling for ActivityThread.h.execute_transaction, which is the Execute method of TransactioonExcutor.
# # #TransactionExcutor
public void execute(ClientTransaction transaction) {... executeCallbacks(transaction); executeLifecycleState(transaction); mPendingActions.clear();if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}
Copy the code
From the excuteCallbacks method, we’ll look at the ExecutelifeccleState method:
# # #TransactioonExcutor
private void executeLifecycleState(ClientTransaction transaction) {
final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
if (lifecycleItem == null) {
// No lifecycle request, return early.
return;
}
final IBinder token = transaction.getActivityToken();
finalActivityClientRecord r = mTransactionHandler.getActivityClient(token); .// Execute the final state
lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}
Copy the code
When you call its excuter method, you’re actually calling the ResumeActivityItem method.
public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
"RESUME_ACTIVITY");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
Copy the code
Finally, the ActivityThread’s handleResumeActivity method is used to call the onResume lifecycle method of the Acitivity that is started.
HandleResumeActivity does the following:
- Call lifecycle: Internally call lifecycle onStart and onResume methods via the performResumeActivity method
- Make the view visible: Add window and make the view visible using the activity.makeVisible method. (So the view is really visible after the onResume method)
Within the performResumeActivity, the Activity’s performResume method is called.
# # #Activity
final void performResume(boolean followedByPause, String reason) {
dispatchActivityPreResumed();
performRestart(true /* start */, reason); .// Run onResume
mInstrumentation.callActivityOnResume(this); EventLogTags.writeWmOnResumeCalled(mIdent, getComponentName().getClassName(), reason); .// Run the Fragment onResume methodmFragments.dispatchResume(); mFragments.execPendingActions(); . dispatchActivityPostResumed(); }Copy the code
Inside has called the performRestart (), is the final Activity onStart (), and then call the mInstrumentation. CallActivityOnResume, is the last of the Activity’s onResume () method.
Well, this time it’s really over.
conclusion
In the process of app startup, four processes are involved: Zygote process, Launcher process, AMS process (SyetemServer process) and application process, as shown in the figure:
Android8.0 Root Activity startup Process
First, the Launcher process asks AMS to create a root Activity. AMS determines whether the application process required by the root Activity exists and starts. If it doesn’t, IT asks Zygote to create the application process. AMS is notified when the application process is ready, and AMS asks the application process to create the root Activity. In the figure above, Step 2 uses Socket communication, and Step 1 and Step 4 use Binder communication.
Binder has a lot of threads running. When a resource in a thread is locked by another resource, the thread information is lost after the fork (fork principle) and there is no key to unlock the lock, resulting in a deadlock. The socket stops other threads and is clean after forking
The whole process of App startup:
- By clicking the app icon, the Launche process calls the startActivitySafely method and internally the startActivity method.
- The startActivity method actually calls the Insturmention execStartActivity method, which is then sent to the ATMS by Binder. The ActivityStart execution handles the Intent and Flag information. ActivityStack then handles the Activity loading process.
- The ActivityStackSupvisor then determines whether the application process required by the root Activity exists and starts. If not, startProcessLocked in AMS is called, and process.start is called internally. Using a socket connection, Zygote’s native method is forked to produce a process called ActivityThread.
- Calling the main method inside the ActivityThread opens a loop. The attchApplication task is initiated inside attach. Do some configuration work in AMS, then have the ActivityThread perform the handlebindApplication operation, and the Application is created and initialized.
- After generally call attachApplication method, internal traverse the activity stack, then call mStackSupervisor. RealStartActivityLocked method, The ApplicationThread is called with Binder to send startup information to the main thread as an H object of type EXECUTE_TRANSACTION and body ClientTransaction, which is actually LaunchActivityItem.
- After getting the launch message, finally call the performLaunchActivity method, call the newActivity method of the Instrumentation class, which creates an Activity2 instance by ClassLoader.
- Finally tell Activity2 to performCreate.
That is, the Application is normally initialized in the handleBindApplication method and is invoked after the process is created. Only a check is done in performLaunchActivity and the exception is created if the Application does not exist.
reference
Exploring the Art of Android Development
Android8.0 root Activity startup process (previous article)
Android8.0 root Activity start process (post)
How to start an Activity on Android10.0
In-process Activity start process source research, based on Android 9.0