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.

  1. Get component information for the Activity to be started from ActivityClientRecord
  2. 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
  3. 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.
  4. 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:

  1. Call lifecycle: Internally call lifecycle onStart and onResume methods via the performResumeActivity method
  2. 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:

  1. By clicking the app icon, the Launche process calls the startActivitySafely method and internally the startActivity method.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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