Detailed explanation of the startup process of Android interview Activity

Startup process of root Activity

The overall process of starting the root Activity is as follows:

It is mainly divided into four parts

  • The Launcher requests ATMS to create a root Activity
  • ATMS will request zygote to create the application process
  • zygote to create application process
  • ATMS requests ApplicationThread to create a root Activity

If we analyze, we will not analyze according to each step above. We will analyze the source code (android 10) according to the following three parts

  • The process by which the Launcher requests ATMS
  • Calling procedure from ATMS to ApplicationThread
  • ActivityThread the process of starting an Activity

The process by which the Launcher requests ATMS

First, click the desktop icon, and the Launcher will call the startactivitysafe function

//Launcher.java 
public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
            @Nullable String sourceContainer) {
        ...
        boolean success = super.startActivitySafely(v, intent, item, sourceContainer);//1
		...
        return success;
    }

A key line of code is that it will call the parent class's startactivitysafe, which will eventually call the methods in the BaseDraggingActivity

//BaseDraggingActivity.java 
public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
            @Nullable String sourceContainer) {
		...
        // Note 1 Prepare intent
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (v != null) {
            intent.setSourceBounds(getViewBounds(v));
        }
        try {
            boolean isShortcut = (item instanceof WorkspaceItemInfo)
                    && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
                    || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                    && !((WorkspaceItemInfo) item).isPromise();
            if (isShortcut) {
                // Shortcuts need some special checks due to legacy reasons.
                startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
            } else if (user == null || user.equals(Process.myUserHandle())) {
                // Could be launching some bookkeeping activity
                if (TestProtocol.sDebugTracing) {
                    android.util.Log.d(TestProtocol.NO_START_TAG,
                            "startActivitySafely 2");
                }
                // Note 2
                startActivity(intent, optsBundle);
                ...
            }
        return false;
    }

Note 1 of the code, which everyone understands, is to start the Activity in a new task stack, and then note 2 is to call startActivity. The code here calls startActivity in Activity

//Activity.java
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        // Note we want to go through this call for compatibility with
        // applications that may have overridden the method.
        startActivityForResult(intent, -1);
    }
}

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
                                   @Nullable Bundle options) {
    //Note 1
    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
            this, mMainThread.getApplicationThread(), mToken, this,
            intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
	...
    }
}

It can be seen from comment 1 that since we started the Activity, the judgment condition of mParent==null is true, and the if statement will enter the if statement, and then the if statement will call mminstrumentation execStartActivity. What does the Instrumentation class do? It is mainly used to monitor the interaction between the system and applications. So here, the next steps go into the Instrumentation class.

@UnsupportedAppUsage
public ActivityResult execStartActivity(
    Context who, IBinder contextThread, IBinder token, Activity target,
    Intent intent, int requestCode, Bundle options) {
	...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        // Note 1
        int result = ActivityTaskManager.getService()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                           intent.resolveTypeIfNeeded(who.getContentResolver()),
                           token, target != null ? target.mEmbeddedID : null,
                           requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

Note 1. Here is the called ActivityTaskManager Getservice (), ActivityTaskManager, is a new class added after Android 10,

//ActivityTaskManager.java	
public static IActivityTaskManager getService() {
        return IActivityTaskManagerSingleton.get();
    }

    @UnsupportedAppUsage(trackingBug = 129726065)
    private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
            new Singleton<IActivityTaskManager>() {
                @Override
                protected IActivityTaskManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE); //Note 1
                    return IActivityTaskManager.Stub.asInterface(b);//Note 2
                }
            };

Here, the binder IPC is used to call * * ActivityTaskManagerService (ATMS) * *.

ATMS is a remote service that needs to be obtained through ServiceManager. The ServiceManager at the bottom of ServiceManager finally calls the ServiceManager at the Native layer. It is the guardian service of Binder. It can obtain the system services registered when the Android system is started, including ATMS mentioned here. The code for obtaining the service is as follows:

//ActivityTaskManager.java
public static IActivityTaskManager getService() {
       return IActivityTaskManagerSingleton.get();
   }

   @UnsupportedAppUsage(trackingBug = 129726065)
   private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
           new Singleton<IActivityTaskManager>() {
               @Override
               protected IActivityTaskManager create() {
                   final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);// Note 1
                   return IActivityTaskManager.Stub.asInterface(b); //Note 2
               }
           };

In note 1, obtain the corresponding system service through the ServiceManager, that is, the ATMS reference of IBinder type. In Note 2, convert it into an object of ActivityTaskManager type. This code uses AIDL, iaactivitytaskmanager The Java class is automatically generated by the AIDL tool at compile time, iaactivitytaskmanager The file path of AIDL is frameworks / base / core / Java / Android / APP / iactivitytaskmanager AIDL. To realize inter process communication, the server, that is, ATMS, only needs to inherit iactivitytaskmanager Stub class and implement the corresponding methods. This completes the process of Launcher requesting ATMS. Let's look at a sequence diagram to review the whole process.

Calling procedure from ATMS to ApplicationThread

Explain the process from Launcher to ATMS. Next, we need to understand the process from ATMS to ApplicationThread. First of all, it should be clear that ATMS runs in the system server process, not in the application process. At this time, our application process has not been up yet. Go on

//ActivityTaskManagerService.java
public final int startActivity(IApplicationThread caller, String callingPackage,
                               Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
                               int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                               resultWho, requestCode, startFlags, profilerInfo, bOptions,
                               UserHandle.getCallingUserId());
}

The startActivityAsUser method will be called in ATMS. It should be noted that the startActivityAsUser method has one more parameter than startActivity, userhandle Getcallinguserid(), ATMS judges the caller's permission based on this parameter.

//ActivityTaskManagerService.java 
public int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
                true /*validateIncomingUser*/);
    }

    int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
            boolean validateIncomingUser) {
        // Note 1
        enforceNotIsolatedCaller("startActivityAsUser");

        //Note 2
        userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        // Note 3
        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setMayWait(userId) //Here mrrequest mayWait = true
                .execute();

    }

In note 1, judge whether the caller process is isolated. If it is isolated, a SecurityException exception will be thrown.

In Note 2, check whether the caller has permission. If not, a SecurityException exception will be thrown.

Note 3 shows that an ActivityStarter is obtained through the ActivityStartController and some parameters are configured. Here, note that the setMayWait method passes in the userId, and the mayWait property of the ActivityStarter will be set to true, which will be used later.

Activity starter is a newly added class in Android 7.0. It is a control class for loading activities. It will collect all logic to decide how to convert Intent and Flags into activities, and associate activities with tasks and stacks. The next step is to enter the logic in the ActivityStarter.

//ActivityStarter.java
int execute() {
    try {
        //Here is true. Enter the if statement
        if (mRequest.mayWait) {
            return startActivityMayWait(mRequest.caller, mRequest.callingUid,
                                        mRequest.callingPackage, mRequest.realCallingPid, mRequest.realCallingUid,
                                        mRequest.intent, mRequest.resolvedType,
                                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
                                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
                                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                                        mRequest.inTask, mRequest.reason,
                                        mRequest.allowPendingRemoteAnimationRegistryLookup,
                                        mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);
        }
        ...
    } finally {
        onExecutionComplete();
    }
}

private int startActivityMayWait(IApplicationThread caller, int callingUid,
        String callingPackage, int requestRealCallingPid, int requestRealCallingUid,
        Intent intent, String resolvedType, IVoiceInteractionSession voiceSession,
        IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, WaitResult outResult,
        Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
        int userId, TaskRecord inTask, String reason,
        boolean allowPendingRemoteAnimationRegistryLookup,
        PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
    // File descriptors are not allowed in intent
    if (intent != null && intent.hasFileDescriptors()) {
        throw new IllegalArgumentException("File descriptors passed in Intent");
    }
    ··· 
    // Parse Intent
    ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
            0 /* matchFlags */,
                    computeResolveFilterUid(
                            callingUid, realCallingUid, mRequest.filterCallingUid));
    ···
    // Analyze the Activity information. When there are multiple choices, a selection box pops up here
    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);

    synchronized (mService.mGlobalLock) {
        ··· 
        // Start Activity
        int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
                voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
                callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
                ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
                allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
                allowBackgroundActivityStart);
        ···
        return res;
    }
}

private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
         String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
         IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
         IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
         String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
         SafeActivityOptions options,
         boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
         TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
         PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
     ···
     // An ActivityRecord object is created here. ActivityRecord is used to describe an Activity and record all the information of the Activity
     // appToken will be built through ActivityRecord and intent at the same time
     ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
             callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
             resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
             mSupervisor, checkedOptions, sourceRecord);
     if (outActivity != null) {
         outActivity[0] = r;
     }

    ......

     final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
             true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);//4
     .....
     return res;
 }

startActivity calls the overload of another startActivity, and then calls the startActivityUnchecked method.

//ActivityStarter.java
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {

        // Set the initial state value, where r is assigned to mStartActivity
        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);
        // Calculate the Flag bit Flag of the start task
        // It mainly deals with some Intent Flag conflicts and reuse problems
        // And the processing of SingleInstance and SingleTask
        computeLaunchingTaskFlags();
  
        // Calculate sourceTask through sourceActivity
        // It mainly deals with FLAG_ACTIVITY_NEW_TASK problem
        computeSourceStack();
  
        mIntent.setFlags(mLaunchFlags);

        // Look for activity records that can be reused
        ActivityRecord reusedActivity = getReusableIntentActivity();
        ···
        if (mReusedActivity != null) {
            ···
            // Move the current stack to the foreground
            mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);
         ···
            setTaskFromIntentActivity(mReusedActivity);

            if (!mAddingToTask && mReuseTask == null) {
                resumeTargetStackIfNeeded();
                return START_TASK_TO_FRONT;
            }
        }
     ···
       //Processing of singleTop or singleInstance
        if (dontStart) {
            // For paranoia, make sure we have correctly resumed the top activity.
            topStack.mLastPausedActivity = null;
            if (mDoResume) {
                mRootActivityContainer.resumeFocusedStacksTopActivities();
            }
            ActivityOptions.abort(mOptions);
            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                // We don't need to start a new activity, and the client said not to do
                // anything if that is the case, so this is it!
                return START_RETURN_INTENT_TO_CALLER;
            }
            ···
            // Distribute a new Intent to an existing Activity
            // Will call back its onNewIntent() method
            deliverNewIntent(top);
          ···
            return START_DELIVERED_TO_TOP;
        }
  
        boolean newTask = false;
        final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                ? mSourceRecord.getTaskRecord() : null;
        // Set the corresponding task
        int result = START_SUCCESS;
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            newTask = true;
            // intent set FLAG_ACTIVITY_NEW_TASK, new task
            result = setTaskFromReuseOrCreateNewTask(taskToAffiliate);
        } else if (mSourceRecord != null) {
            // Set the stack where sourceRecord is located, that is, the standard startup mode
            result = setTaskFromSourceRecord();
        } else if (mInTask != null) {
            // Specify the taskAffinity to start and set it to the corresponding task
            result = setTaskFromInTask();
        } else {
            // This does not happen if you start in the task in the current focus
            result = setTaskToCurrentTopOrCreateNewTask();
        }
        if (result != START_SUCCESS) {
            return result;
        }
        ··· 
        // mDoResume is passed in from outside, which is true in this process
        if (mDoResume) {
            // Use the ActivityStaskSupervisor to display the Activity
            final ActivityRecord topTaskActivity =
                    mStartActivity.getTaskRecord().topRunningActivityLocked();
            if (!mTargetStack.isFocusable()
                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay
                    && mStartActivity != topTaskActivity)) {

                mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS);

                mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
            } else {
                // If the current activity can get the focus, but the current Stack does not get the focus
                if (mTargetStack.isFocusable()
                        && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
                    // Move the corresponding Task to the foreground
                    mTargetStack.moveToFront("startActivityUnchecked");
                }
                 // Make the activity at the top of the stack visible, that is, the resume state
                 mRootActivityContainer.resumeFocusedStacksTopActivities(
                        mTargetStack, mStartActivity, mOptions);
            }
        } else if (mStartActivity != null) {
            mSupervisor.mRecentTasks.add(mStartActivity.getTaskRecord());
        }
        mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);

        mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTaskRecord(),
                preferredWindowingMode, mPreferredDisplayId, mTargetStack);

        return START_SUCCESS;
    }

The startActivityUnchecked method mainly deals with the logic related to branch management. In the previous analysis, we set Flag to Flag when setting_ ACTIVITY_ NEW_ Task, that is, it will execute settaskfromreuseorcreatewtask, and a new TaskRecord will be created to describe the activity task stack. Activity money is actually an imaginary model, which does not really exist. Then resumeFocusedStacksTopActivities will be called, and the Activity in the resumeFocusedStacksTopActivities method will be called ActivityStack#resumeTopActivityUncheckedLocked to start the top of the stack.

// ActivityStack.java
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mInResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }

        boolean result = false;
        try {
            // Protect against recursion. Prevent recursion and ensure that only one Activity executes the method
            mInResumeTopActivity = true;
            result = resumeTopActivityInnerLocked(prev, options);
            final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
            if (next == null || !next.canTurnScreenOn()) {
                checkReadyForSleep();
            }
        } finally {
            mInResumeTopActivity = false;
        }

        return result;
    }

mInResumeTopActivity = true; Used to ensure that only one Activity at a time performs the resumeTopActivityUncheckedLocked() operation. Then call topRunningActivityLocked

// ActivityStack.java
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    ···
    // Get the un finish ed activity at the top of the stack
    ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
    ···
    if (next.attachedToProcess()) {
        ···
    } else {
        ···
        mStackSupervisor.startSpecificActivityLocked(next, true, true);
    }
}

The logic of the resumetoperactivityinnerlocked method is very complex. It is mainly to judge whether the resume stack top activity is really needed at present. It is simplified here. When our top stack activity is not bound to any Application, it will call ActivityStackSupervisor#startSpecificActivityLocked. This is exactly the case at this time.

// ActivityStackSupervisor.java
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
    // Obtain the WindowProcessController object of the target process through ATMS
    // WindowProcessController is used to synchronize process status between ActivityManager and WindowManager
    // If wpc is empty, the corresponding process does not exist or is not started
    final WindowProcessController wpc =
            mService.getProcessController(r.processName, r.info.applicationInfo.uid);

    boolean knownToBeDead = false;
    // The hashthread here represents the iaapplicationthread
    if (wpc != null && wpc.hasThread()) {
        try {
            // Start activity
            realStartActivityLocked(r, wpc, andResume, checkConfig);
            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception when starting activity "
                    + r.intent.getComponent().flattenToShortString(), e);
        }

        // If a dead object exception was thrown -- fall through to
        // restart the application.
        knownToBeDead = true;
    }

    // This mainly deals with the keyboard lock problem
    if (getKeyguardController().isKeyguardLocked()) {
        r.notifyUnknownVisibilityLaunched();
    }

    try {
        ···
        // If the corresponding App process has not been started
        // Start the process by sending a message through the handler
        final Message msg = PooledLambda.obtainMessage(
                ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
                r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
        mService.mH.sendMessage(msg);
    } finally {
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }
}

Here we know that if our application process is not started, we need to start the application process by sending a message. We won't talk about this part of the process here. The statement that actually starts the Activity is the method realStartActivityLocked()

// ActivityStackSupervisor.java
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
        boolean andResume, boolean checkConfig) throws RemoteException {
            ···
            // Create a start Activity transaction
            final ClientTransaction clientTransaction = ClientTransaction.obtain(
                    proc.getThread(), r.appToken);

            final DisplayContent dc = r.getDisplay().mDisplayContent;
            // Add the Callback. Note that the LaunchActivityItem is created here
            clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                    System.identityHashCode(r), r.info,
                    // TODO: Have this take the merged configuration instead of separate global
                    // and override configs.
                    mergedConfiguration.getGlobalConfiguration(),
                    mergedConfiguration.getOverrideConfiguration(), r.compat,
                    r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                    r.icicle, r.persistentState, results, newIntents,
                    dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                            r.assistToken));

            // Set the final state that this transaction should execute
            // This process will be set to resume, indicating that the activity should be executed to onResume
            final ActivityLifecycleItem lifecycleItem;
            if (andResume) {
                lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
            } else {
                lifecycleItem = PauseActivityItem.obtain();
            }
            clientTransaction.setLifecycleStateRequest(lifecycleItem);

            // Execute transaction
            mService.getLifecycleManager().scheduleTransaction(clientTransaction);
    ···
    return true;
}

A start Activity transaction is created in the realStartActivityLocked method and sent to the target App using iaapplicationthread. Starting from Android 8.0, the start of Activity will be completed through transactions, which will be remotely sent to the target App through the iaapplicationthread of the target App, and then executed through clientllifecycle manager. At this point, the logic executed in ATMS is over, and the rest is the ApplicationThread of the target App to execute each life cycle method of the target Activity.

ActivityThread starts the Activity process

After a series of calls, the start of Activity comes to the target App.

As we know from the above, this process starts from the transaction passed by Binder communication received by ApplicationThread. ApplicationThread#scheduleTransaction calls ActivityThread#scheduleTransaction. Let's look directly at the scheduleTransaction function of ActivityThread

// ActivityThread.java
@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
    ActivityThread.this.scheduleTransaction(transaction);
}

The ActivityThread#scheduleTransaction method is defined in its parent class ClientTransactionHandler.

//ClientTransactionHandler.java
void scheduleTransaction(ClientTransaction transaction) {
    transaction.preExecute(this);
    sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}

Here, the sendMessage method is called to send a what is activitythread to the main thread H.EXECUTE_ Transaction and deliver the transaction.

ApplicationThread runs in Binder thread, and communication with ActivityThread needs to go through Handler. H is an internal class in ActivityThread, which inherits from Handler. When ActivityThread calls sendMessage method, it is actually calling H#sendMessage.

//ActivityThread.java
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 is an instance of class H
    mH.sendMessage(msg);
}

If mH sends a message, it will be received and processed in the handlerMessage of class h, so we can take a look at the handlerMessage method of class H

//ActivityThread.H.java
public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        // Omit some case s
        ···
        case EXECUTE_TRANSACTION:
            final ClientTransaction transaction = (ClientTransaction) msg.obj;
            mTransactionExecutor.execute(transaction);
            if (isSystem()) {
                // Client transactions inside system process are recycled on the client side
                // instead of ClientLifecycleManager to avoid being cleared before this
                // message is handled.
                transaction.recycle();
            }
            // TODO(lifecycler): Recycle locally scheduled transactions.
            break;
        // Omit some case s
        ···
    }
    Object obj = msg.obj;
    if (obj instanceof SomeArgs) {
        ((SomeArgs) obj).recycle();
    }
    if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}

Where TransactionExecutor#execute is used to execute transactions.

TransactionExecutor is a class dedicated to processing transactions to ensure that transactions are executed in the correct order.

//TransactionExecutor.java
public void execute(ClientTransaction transaction) {
    if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");
    // appToken in ActivityRecord
    final IBinder token = transaction.getActivityToken();
    ···
    if (DEBUG_RESOLVER) Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
    // Execute all callbacks
    executeCallbacks(transaction);
    // Execute to corresponding lifecycle
    executeLifecycleState(transaction);
    mPendingActions.clear();
    if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}

Previously, we created a transaction in the ActivityStackSupervisor and added it to the ClientTransaction by addCallback. Now we call executeCallbacks to execute each Callback.

//TransactionExecutor.java
public void executeCallbacks(ClientTransaction transaction) {
    final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
    if (callbacks == null || callbacks.isEmpty()) {
        // Fast return path
        return;
    }
	...
    // Execute the extract of each item in a loop    
    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) {
            // This method can smooth the execution life cycle
            cycleToPath(r, closestPreExecutionState, transaction);
        }
        // Execute the item's execute method
        item.execute(mTransactionHandler, token, mPendingActions);
        item.postExecute(mTransactionHandler, token, mPendingActions);
	...
    }
}

In the previous section, we know that the ClientTransactionItem created when starting the Activity is * * LaunchActivityItem * *, and its execute method will be executed later. So let's take a look at the implementation of the execute method in LaunchActivityItem

//LaunchActivityItem.java
@Override
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);
    // Handle matters before starting an Activity
    client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

client is ActivityThread. In the method, ActivityThread#handleLaunchActivity is called to start Activity.

//ActivityThread.java
public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, Intent customIntent) {
    ···
    // Initialize WindowManager
    WindowManagerGlobal.initialize();
    ···
    // Start Activity
    final Activity a = performLaunchActivity(r, customIntent);
    ···
    return a;
}

The core code started by the Activity is in the performLaunchActivity method.

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ···
    // Create the context in which you want to start the Activity
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        // Create the Activity instance with the class loader, that is, through reflection
        java.lang.ClassLoader cl = appContext.getClassLoader();
        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 {
        // Create Application instance
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        ···
        if (activity != null) {
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config = new Configuration(mCompatConfiguration);
            if (r.overrideConfig != null) {
                config.updateFrom(r.overrideConfig);
            }
            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                    + r.activityInfo.name + " with config " + config);
            Window window = null;
            if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                window = r.mPendingRemoveWindow;
                r.mPendingRemoveWindow = null;
                r.mPendingRemoveWindowManager = null;
            }
            appContext.setOuterContext(activity);
            // Call activity Initialize attach
            // Establish the connection between Context and Activity, and Window will be created here
            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);

            ···
            // Callback onCreate
            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;
        }
        // Update LifeCycle status
        r.setState(ON_CREATE);
        ···
    return activity;
}

At this point, the Activity has been successfully created and its life cycle related methods have been executed.

The above is the start process of the whole root Activity.

summary

Corresponding to the first figure above, the startup of the root Activity involves a total of four processes. Launcher process, SystemServer process, Zygote process, and application process.

First, the Launcher process will request ATMS to create an Activity, and ATMS will judge whether the application process required by the root Activity exists and start it. If it does not exist, the Zygote process is requested to create an application process. After the application process starts, ATMS will request the application process to create a root Activity and start it. Step 1 is cross process communication through Binder, step 2 is communication through socket, and step 4 is Binder communication.

reference resources:
1. Android advanced decryption
2,Activity start process

Keywords: Android Interview source code

Added by mATOK on Thu, 13 Jan 2022 12:59:14 +0200