This is the 7th day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021

(Android11.0) In-depth mining of App startup process (Part 1)

(Android11.0) In-depth mining of App startup process (Part 2)

preface

The startup of an Activity is divided into two types, one is the root Activity startup process, the other is the normal Activity startup process. The first is the process of starting an app. A normal Activity is the process of calling startActivity in the app.

Take the root of the start of the Activity is relatively comprehensive, but also a good understanding of the entire startup process of Android. There are three parts to the request Launcher process, the call from AMS to ApplicationThread, and the ActivityThread that starts the Activity.

The android7, 8, 9 start the process to see again, then we are now based on android 11 source code to analyze again, to choose the latest to choose the analysis, design ideas are all common.

Launcher process

When you click the app icon, the startActivitySafely method of the Launcher is called, and the startActivity method is called internally.

## Launcher.java
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {...// Set to start in a new task stack
     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     if(v ! =null) {
         intent.setSourceBounds(getViewBounds(v));
     }
     try {
         if (Utilities.ATLEAST_MARSHMALLOW
                 && (item instanceof ShortcutInfo)
                 && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
                  || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                 && !((ShortcutInfo) item).isPromise()) {
             startShortcutIntentSafely(intent, optsBundle, item);
         } else if (user == null || user.equals(Process.myUserHandle())) {
           // Call startActivity to start the page
             startActivity(intent, optsBundle);/ / 2
         } else {
             LauncherAppsCompat.getInstance(this).startActivityForProfile(
                     intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
         }
         return true;
     } catch (ActivityNotFoundException|SecurityException e) {
         Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
         Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
     }
     return false;
 }
Copy the code

FLAG_ACTIVITY_NEW_TASK sets the intent Flag to intent. FLAG_ACTIVITY_NEW_TASK to start a new task stack, which actually calls the startActivity method of the activity.

# # #Activity
      public void startActivity(Intent intent, @Nullable Bundle options) {...if(options ! =null) {
            startActivityForResult(intent, -1, options);
        } else {
            // We want this call to be compatible with applications that may have overridden the method.
            startActivityForResult(intent, -1); }}Copy the code

It goes to the startActivityForResult method, and the second parameter is -1, which means the Launcher doesn’t need to know the result of the Activity’s launch.

### Activitu.java   
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); . cancelInputsAndStartExitTransition(options); }else{... }}Copy the code

Since it is the first time that mParent = NULL is created, the Instrumentation execStartActivity method is called internally. Instrumentation has methods for tracking applications and life cycles.

# # #Instrumentation    
public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {...try {
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);
            intresult = ActivityTaskManager.getService().startActivity(whoThread, who.getBasePackageName(), who.getAttributionTag(), 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;
    }
Copy the code

(ActivityTaskManager andrrod 10.0 is a new method, getService is a new method since Android 8.0)

Will first call ActivityTaskManager. GetService () for AMS proxy object, then call startActivity method, that is how to get to the AMS proxy objects?

# # #ActivityTaskManager
 public static IActivityTaskManager getService(a) {
        return IActivityTaskManagerSingleton.get();
    }
    
 private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
            new Singleton<IActivityTaskManager>() {
                @Override
                protected IActivityTaskManager create(a) {
                  // Get a reference to ACTIVITY_TASK_SERVICE, which is ATMS of type IBinder
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                  // To implement interprocess communication, the server (AMS) simply inherits the iActivityManager.stub class and implements the corresponding methods.
                    returnIActivityTaskManager.Stub.asInterface(b); }};Copy the code

You can see here for a service across processes, namely ActivityTaskManagerService * * * *, it is inherited from IActivityTaskManager. Stub, a Binder object. Now that you know what this is, you can go back to the execStartActivity method of the Instrumentation and look below.

ATMS to ApplicationThread process

Then we enter the service ATMS provided by the system, and then the process of calling THE ATMS to ApplicationThread. The sequence diagram is as follows:

Moving on, you’ve reached the startActivity method of ATMS.

# # #ActivityTaskManagerService 
 public final int startActivity(IApplicationThread caller, String callingPackage,
            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
            Bundle bOptions) {
   / / startActivityAsUser methods can be found much startActivity method. A parameter UserHandle getCallingUserId (), this method can obtain the caller's UserId
   //AMS determines the caller's permissions based on this UserId.
        return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }

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

  private int startActivityAsUser(IApplicationThread caller, String callingPackage,
            @Nullable String callingFeatureId, Intent intent, String resolvedType,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
        assertPackageMatchesCallingUid(callingPackage);
    // Determine whether the caller process is quarantined
        enforceNotIsolatedCaller("startActivityAsUser");

    // Check caller permissions
        userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        // Get the ActivityStart object and excute the activity at the end.
        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setCallingFeatureId(callingFeatureId)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setUserId(userId)
                .execute();

    }
Copy the code

One layer after layer set call, come to the last, through etActivityStartController () obtainStarter ActivityStart object, and in the final excute method to start the activity.

### ActivityStarter
// Execute the request to start the activity
int execute(a) {
        try{...if (mRequest.activityInfo == null) {
                mRequest.resolveActivity(mSupervisor);
            }

            int res;
            synchronized (mService.mGlobalLock) {
                final booleanglobalConfigWillChange = mRequest.globalConfig ! =null&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) ! =0;
                finalActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack(); .final long origId = Binder.clearCallingIdentity();

                res = resolveToHeavyWeightSwitcherIfNeeded();
                if(res ! = START_SUCCESS) {return res;
                }
              
              // Execute the boot interface requestres = executeRequest(mRequest); Binder.restoreCallingIdentity(origId); .return getExternalResult(mRequest.waitResult == null? res : waitForResult(res, mLastStartActivityRecord)); }}finally{ onExecutionComplete(); }}Copy the code

(executeRequest is a new method in Android11)

Moving on to the executeRequest method, focus on the main methods:

### ActivityStarter.java
  
  // Execute the activity start request to start the activity start tour. The first is to perform a few preliminary checks. The normal activity initiation process goes through
  private int executeRequest(Request request) {... mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession, request.voiceInteractor, startFlags,true /* doResume */, checkedOptions, inTask,
                restrictedBgActivity, intentGrants);

        if(request.outActivity ! =null) {
            request.outActivity[0] = mLastStartActivityRecord;
        }

        return mLastStartActivityResult;
}
Copy the code

And then I call the startActivityUnchecked method.

###   ActivityStarter
// Handle stack management related logic
// Start an activity after initially checking and confirming that the caller has the necessary permissions to perform the operation
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, Task inTask,
                boolean restrictedBgActivity, NeededUriGrants intentGrants) {
        int result = START_CANCELED;
        final ActivityStack startedActivityStack;
        try {
            mService.deferWindowLayout();
            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
            result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
            startedActivityStack = handleStartResult(r, result);
            mService.continueWindowLayout();
        }

        postStartActivityProcessing(r, result, startedActivityStack);

        return result;
    }
Copy the code

StartActivityUnchecked handles the stack management logic. Called after the startActivityInner method, the last call to RootWindowContainer resumeFocusedStacksTopActivities method.

### ActivityStarter
  // Start the activity and determine whether the activity should be added to the top of an existing task or pass a new intent to an existing activity. Active tasks are also manipulated onto requested or valid stack displays.
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, Task inTask,
            boolean restrictedBgActivity, NeededUriGrants intentGrants) {...if (newTask) {
            finalTask taskToAffiliate = (mLaunchTaskBehind && mSourceRecord ! =null)? mSourceRecord.getTask() :null;
    // A new Activity stack is created internally
            setNewTask(taskToAffiliate);
            if (mService.getLockTaskController().isLockTaskModeViolation(
                    mStartActivity.getTask())) {
                Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
                returnSTART_RETURN_LOCK_TASK_MODE_VIOLATION; }}else if (mAddingToTask) {
            addOrReparentStartingActivity(targetTask, "adding to task"); }...// Focus on this line of code
    mRootWindowContainer.resumeFocusedStacksTopActivities(
                        mTargetStack, mStartActivity, mOptions);  1.return START_SUCCESS;
            }
Copy the code

RootWindowContainer is a new class for Android10

RootWindowContainer shares some of the functions of the previous ActivityStackSupervisor. ResumeFocusedStacksTopActivities internal will call ActivityStack resumeTopActivityUncheckedLocked method, then we go in to see this method.

# # #ActivityStack
      boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mInResumeTopActivity) {
            // Do not process if the top Activity is executing
            return false;
        }

        boolean result = false;
        try {
            // Protect against recursion.
            mInResumeTopActivity = true;
            result = resumeTopActivityInnerLocked(prev, options);

            // To resume Top Activity, you may need to pause Top Activity
            final ActivityRecord next = topRunningActivity(true /* focusableOnly */);
            if (next == null || !next.canTurnScreenOn()) {
                checkReadyForSleep();
            }
        } finally {
            mInResumeTopActivity = false;
        }

        return result;
    }
Copy the code

Then jump to resumeTopActivityInnerLocked method to see:

# # #ActivityStack
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {...if(mResumedActivity ! =null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
    // When the previous interface is active, the previous interface is paused
            pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next); }...finalClientTransaction transaction = ClientTransaction.obtain(next.app.getThread(), next.appToken); .// The Activity that starts sends the ResumeActivityItem transaction to the clienttransaction.setLifecycleStateRequest( ResumeActivityItem.obtain(next.app.getReportedProcState(), dc.isNextTransitionForward())); mAtmService.getLifecycleManager().scheduleTransaction(transaction); .if(! next.hasBeenLaunched) { next.hasBeenLaunched =true;
            } else {
                if (SHOW_APP_STARTING_PREVIEW) {
                  // A blank screen is displayed on cold startup
                    next.showStartingWindow(null /* prev */.false /* newTask */.false /* taskSwich */);
                }
                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
    		// Continue the current Activity. Normal Activity starts normally
               mStackSupervisor.startSpecificActivity(next, true.true); }...return true;
}
Copy the code

(This method has more than 300 lines really many!!) After pausing the previous Activity and judging the previous interface, call next. ShowStartingWindow to display a Window before starting the Activity. And then call mStackSupervisor startSpecificActivity, a normal boot.

# # #ActivityStackSupervisor
   void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) { 
        final WindowProcessController wpc =
                mService.getProcessController(r.processName, r.info.applicationInfo.uid);

        boolean knownToBeDead = false;
  // Whether the application process is started
        if(wpc ! =null && wpc.hasThread()) {
            try {
              // Finally see the actual method to start the activity
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

            knownToBeDead = true;
        }

        r.notifyUnknownVisibilityLaunchedForKeyguardTransition();

        final boolean isTop = andResume && r.isTopRunningActivity();
  			/ / todo analysis
        mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
    }
  
Copy the code

Let’s take a look at realStart Active Locked in a minute, kind of impatient.

### ActivityStackSupervisor.java
    boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
            boolean andResume, boolean checkConfig) throws RemoteException {...final Task task = r.getTask();
        final ActivityStack stack = task.getStack();

        beginDeferResume();

        try{...final MergedConfiguration mergedConfiguration = new MergedConfiguration(
                        proc.getConfiguration(), r.getMergedOverrideConfiguration());
                r.setLastReportedConfiguration(mergedConfiguration);
                logIfTransactionTooLarge(r.intent, r.getSavedState());

                // Create the activity to start the transaction
          //proc.getThread (IApplicationThread) is a proxy for the ApplicationThread.
                final ClientTransaction clientTransaction = ClientTransaction.obtain(
                        proc.getThread(), r.appToken);

                final DisplayContent dc = r.getDisplay().mDisplayContent;
                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()));

                // Set the desired final state
                final ActivityLifecycleItem lifecycleItem;
                if (andResume) {
                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
                } else {
                    lifecycleItem = PauseActivityItem.obtain();
                }
              // The lifecycle state that the client should be in after executing a transaction.
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

                // Finally IAppliicatioinThread implements cross-processmService.getLifecycleManager().scheduleTransaction(clientTransaction); .return true;
    }
Copy the code

ClientTransaction contains a set of messages that can be sent to the client. This includes the callback list and the final lifecycle state. Clienttransaction.obtain (proc.getThread(), r.aptoken) where proc.getThread is the IApplicationThread, Is the proxy for the ApplicationThread mentioned earlier in the system process.

Next clientTransaction addCallback method, the incoming LaunchActivityItem, that we come to the specific look at LaunchActivityItem. Obtain adjustable?

### LaunchActivityItem
  // Gets an instance initialized with the supplied parameters.
public static LaunchActivityItem obtain(Intent intent, 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 isForward, ProfilerInfo profilerInfo,
            IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments) {
        LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
        if (instance == null) {
            instance = new LaunchActivityItem();
        }
        setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer,
                voiceInteractor, procState, state, persistentState, pendingResults,
                pendingNewIntents, isForward, profilerInfo, assistToken, fixedRotationAdjustments);

        return instance;
    }

Copy the code

This can be done before startup. How did you finally successfully start the Activity?

That should see this method under the mService. GetLifecycleManager () scheduleTransaction (clientTransaction), Here is mService ActivityTaskManagerService, call getLifeCycleManager method, obtained the ClientLifecycleManager, call again scheduleTransaction inside.

# # #ClientLifecycleManager
 void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        if(! (clientinstanceofBinder)) { transaction.recycle(); }}Copy the code

Take a look at what transaction.schedule() does again.

# # #ClientTransaction
   public void schedule(a) throws RemoteException {
  // It will be sent to the client
        mClient.scheduleTransaction(this);
    }
Copy the code

Seeing that the scheduleTransaction method of IApplicationThread is called, it is clear that the start operation crosses the process to the client. Because the IApplicationThread is a proxy for the ApplicationThread in the system process, the actual execution is in the client ApplicationThread. In other words, the Application is the communication bridge between the process where the ATMS resides and the Application process.

conclusion

Here we have learned about the ATMS process of the request for the Launcher and the process of starting an ApplicationThread. Part of the ActivityThread that starts an Activity will be analyzed in the next chapter.

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