Based on Android 10 source code analysis

preface

The analysis of the Activity launch process is mainly done with the following two pointcuts

  • Analysis of the entire startup process
  • Code tracking and process detail analysis.

Because our AMS Activity startup process is very complex, and the code flow is very jumping. If the analysis directly from our code will be more difficult to understand, so before entering the code flow analysis activity start process, we need to have a general understanding of the whole start process, so as to help us to analyze the source code.

Please go to the startup process of AMS

Overview of the Activity startup process

If the startup of an Activity is divided according to process differentiation, it can be divided into:

  • The started Activity process does not exist, for example, a Launcher app
  • The started Activity process exists

The difference between them and:

  1. AMS tells Zygote to fork a new app process when the Activity does not exist.
  2. After the app process starts, tell the AMS process to complete the fork and enter the follow-up Activity start process.

App startup process diagram

Communication mode

When you start a new app, there are many ways to communicate between processes. As shown in figure

Socket

Zygote creates a local Socket communication service ZygoteServer during process initialization. Zygote fork is notified through the socket.

//ZygoteInit.java
public static void main(String argv[]) {
    ZygoteServer zygoteServer = null;
  	Runnable caller;
    try {
    	// Create a Socket service that communicates with the System Server process
			zygoteServer = new ZygoteServer(isPrimaryZygote);
  		// If a child process is forked, a non-null caller is returned
  		caller = zygoteServer.runSelectLoop(abiList);
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        throw ex;
    } finally {
        if(zygoteServer ! =null) { zygoteServer.closeServerSocket(); }}// We're in the child process and have exited the select loop. Proceed to execute the
    // command.
  	// If caller! = null indicates that the current process is a child process. Execute the following method to enter ActivityThread
    if(caller ! =null) { caller.run(); }}Copy the code

ZygoteServer. RunSelectLoop () will deal with all kinds of messages including socket connection, USAP

Binder communication

App and AMS communicate with each other through the Binder. When the app process forks, AMS needs to be told that the app process has been created successfully and the IApplicationThread Binder object is passed to AMS.

  1. App gets AMS Binder proxy
  2. App passes ApplicationThread Binder to AMS
  3. AMS saves the ApplicationThread Binder Proxy into ProcessRecord
Obtain the AMS Binder agent

Because AMS service at the time of initialization has to service registry to ServiceManager, so can pass ActivityManager. GetService () to obtain IActivityManager. Stub. The Proxy agent object, You can then communicate with the binder

//ActivityManager.java
public static IActivityManager getService(a) {
    return IActivityManagerSingleton.get();
}
// Singleton mode
private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create(a) {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;/ / return IActivityManager. Stub. Proxy Proxy objects}};Copy the code
Pass the ApplicationThread Binder object to AMS

ApplicationThread inherits from iApplicationThread.stub. After ApplicationThread Binder instance has been created, has not registered to the ServiceManager, AMS unable to actively seek our IApplicationThread. The Stub. The Proxy object, Therefore, we have to pass our Binder by way of reference.

//ActivityThread.java
@UnsupportedAppUsage
// create ApplicationThread Binder
final ApplicationThread mAppThread = new ApplicationThread();
public static void main(String[] args) {
    / /...
  	/ / 1. Create ActivityThread
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
  	//....
}
private void attach(boolean system, long startSeq) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if(! system) {/ /...
      	//3. Obtain AMS binder proxy
        final IActivityManager mgr = ActivityManager.getService();
        try {
          	//4. The mAppThread binder parameter is passed to AMS
            mgr.attachApplication(mAppThread, startSeq);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        / /...
    } else {
        / / does not perform
    }
		/ /...
}
Copy the code
AMS saves the ApplicationThread Binder Proxy into ProcessRecord

Through the call binder, eventually into ActivityManagerService. AttachApplication

//ActivityManagerService.java
/ / note This thread is IApplicationThread. Styb. The Proxy object, after transformation Binder driver has to help us better
public final void attachApplication(IApplicationThread thread, long startSeq) {
    / /...
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
      	/ / 1. Call attachApplicationLockedattachApplicationLocked(thread, callingPid, callingUid, startSeq); Binder.restoreCallingIdentity(origId); }}private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {

    ProcessRecord app;
		//.... 
    try {
      	//2.AppDeathRecipient 保存 thread
        AppDeathRecipient adr = new AppDeathRecipient(
                app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
      	/ / 3. AppDeathRecipient ProcessRecord kept
        app.deathRecipient = adr;
    } catch (RemoteException e) {
      	/ /...
        return false;
    }
  	/ /...
    return true;
}

Copy the code

As shown in ProcessRecord, AMS saves the BINDER agent for the App process ApplicationThread. Later in the process you can communicate with the App process through ApplicationThread’s Binder agent.

Introduction to other core data classes

ProcessRecord

The interior of Android system is very complicated. After layers of encapsulation, APP only needs a few lines of simple code to complete the start/end and life cycle operations of any component.

When a component is started, it first needs to depend on the process, so the process needs to be created first, and the system needs to record each process, resulting in ProcessRecord.

Refer to the link

ActivityRecord

ActivityRecord, An entry in the history stack, representing An activity. An entry in the history stack that represents an activity.

Activity information is recorded in an ActivityRecord object

ActivityStack

ActivityStack, which internally maintains an ArrayList to manage ActivityRecords

ActivityStackSupervisor

ActivityStackSupervisor, as its name suggests, is used to manage ActivityStack

instrumentation

The official description

instrumentation can load both a test package and the application under test into the same process. Since the application components and their tests are in the same process, the tests can invoke methods in the components, and modify and examine fields in the components.

The translation

Instrumentation can load the test package and the target test application into the same process. Since each control and the test code are running in the same process, the test code can of course call the control’s methods and modify and validate some of the control’s data

Android instrumentation is a set of control methods or “hooks” in the Android system. These hooks can control the running of Android controls outside the normal life cycle (normal is controlled by the operating system), in fact, refers to the various process control methods provided by the Instrumentation class, the following table shows the relationship between some methods

Method Control by User(Instrumentation) Control by OS
onCreate callActivityOnCreate onCreate
onDestroy callActivityOnDestroy onDestroy
onStart callActivityOnStart onStart

Refer to the link

When an Activity is started, the lifecycle of the Activity is called with Instrumentation

Activit start

If the Activity startup process is broken down by process, it can be broken down into the following steps:

Let’s take app process A starting app process B as an example

  • Process A sends A command to AMS to dynamically start process B’s activity (triggered)
  • AMS handles distribution startup commands (relay)
    • If (process B does not exist) fork process B
      • Notifies AMS B that the process is successfully created
    • Send the start Activity command to process B
  • Process B handles AMS startup commands (process)

Trigger (entrance)

Activity::startActivityForResult

All of the acitivity prime will eventually go into this method

//Activity.java
public void startActivityForResult(
        String who, Intent intent, int requestCode, @Nullable Bundle options) {
		/ /...
  	// Initiate Actiivity by mInstrumentation
    Instrumentation.ActivityResult ar =
        mInstrumentation.execStartActivity(
            this, mMainThread.getApplicationThread(), mToken, who,
            intent, requestCode, options);
  	// When there is a result to process
    if(ar ! =null) {
        mMainThread.sendActivityResult(
            mToken, who, requestCode,
            ar.getResultCode(), ar.getResultData());
    }
    / /...
}
Copy the code

MMainThread. GetApplicationThread () and ginseng ApplicationThread binder is wide

Instrumentation::execStartActivity

public ActivityResult execStartActivity(
    Context who, IBinder contextThread, IBinder token, String target,
    Intent intent, int requestCode, Bundle options) {
    IApplicationThread whoThread = (IApplicationThread) contextThread;
		/ /...
    try {
        intent.migrateExtraStreamToClipData(who);
        intent.prepareToLeaveProcess(who);
      	Obtain the ActivityManagerService agent 2. Start the activity
        int result = ActivityTaskManager.getService().startActivity(whoThread,
                who.getBasePackageName(), who.getAttributionTag(), intent,
                intent.resolveTypeIfNeeded(who.getContentResolver()), token, target,
                requestCode, 0.null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}
Copy the code
ActivityTaskManager.getService()

Android 10 began the Activity will be start the service logic from AMS to ActivityTaskManagerService behind (ATMS). The logic is the same as before

public static IActivityTaskManager getService(a) {
    return IActivityTaskManagerSingleton.get();
}

@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
        new Singleton<IActivityTaskManager>() {
            @Override
            protected IActivityTaskManager create(a) {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                returnIActivityTaskManager.Stub.asInterface(b); }};Copy the code

Relay (process distribution start commands)

ActivityTaskManagerService.startActivity()

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) {
  	/ / UserHandle getCallingUserId () by getting binder. The getCallngUid to obtain userId
    return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
            resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
            UserHandle.getCallingUserId());
}
Copy the code

ActivityTaskManagerService.startActivityAsUser()

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) {
    / /...
		//validateIncomingUser == true Verifies the userId
    userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
            Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
    // Constructor design mode executes activitystarter.execute
    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
GetActivityStartController (.) checkTargetUser () (ignore)

ValidateIncomingUser == True Verifies the userId

ActivityStartController.checkTargetUser()

. = > ActivityTaskManagerService handleIncomingUser () obtained by LocalServices ActivityManagerService. LocalService instance. As mentioned in the startup process of AMS, LocalServices cache the internal implementation of LocalService for each service

​ =>ActivityManagerService.LocalService.handleIncomingUser()

= > UserController. HandleIncomingUser () really perform validation, normal boot process returns in userId

ActivityStartController.obtainStarter()

The ActivityStarter is created in project mode. Mfactory.obtain () reuse ActivityStarter to reduce GC frequency in the same way that Handler message.obtain () does

ActivityStarter obtainStarter(Intent intent, String reason) {
    return mFactory.obtain().setIntent(intent).setReason(reason);
}
Copy the code
ActivityStarter.execute()
int execute(a) {
    try {
       / /...
        // If the caller hasn't already resolved the activity, we're willing
        // to do so here. If the caller is already holding the WM lock here,
        // and we need to check dynamic Uri permissions, then we're forced
        // to assume those permissions are denied to avoid deadlocking.
      	// Determine the Activity that can handle the start command.
        if (mRequest.activityInfo == null) {
            mRequest.resolveActivity(mSupervisor);
        }

        int res;
        synchronized (mService.mGlobalLock) {
            / /...
          	// Start executing the request
            res = executeRequest(mRequest);

            Binder.restoreCallingIdentity(origId);

            if (globalConfigWillChange) {
                // If the caller also wants to switch to a new configuration, do so now.
                // This allows a clean switch, as we are waiting for the current activity
                // to pause (so we will not destroy it), and have not yet started the
                // next activity.
                mService.mAmInternal.enforceCallingPermission(
                        android.Manifest.permission.CHANGE_CONFIGURATION,
                        "updateConfiguration()");
                if(stack ! =null) {
                    stack.mConfigWillChange = false;
                }
                if (DEBUG_CONFIGURATION) {
                    Slog.v(TAG_CONFIGURATION,
                            "Updating to new configuration after starting activity.");
                }
                mService.updateConfigurationLocked(mRequest.globalConfig, null.false);
            }

            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
                    mLastStartActivityRecord);
            return getExternalResult(mRequest.waitResult == null? res : waitForResult(res, mLastStartActivityRecord)); }}finally{ onExecutionComplete(); }}Copy the code
ActivityStarter.Request.resolveActivity()
void resolveActivity(ActivityStackSupervisor supervisor) {
    / /...
  	// Copy an intent, even if the intent changes ephemeralIntent
    ephemeralIntent = new Intent(intent);
  	// Make sure that intent changes outside the method or changes here do not affect each other
    intent = new Intent(intent);
    / /...
    // Collect information about the target of the Intent.
  	For example, if we open a webView with an Intent and install multiple browsers, then the Request will be collected after this method is completed
    activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,
            profilerInfo);
		/ /...
}
Copy the code
ActivityStarter.executeRequest()
private int executeRequest(Request request) {
    / /...
  	// Find the callerApp
  	WindowProcessController callerApp = null;
    if(caller ! =null) {
        callerApp = mService.getProcessController(caller);
        if(callerApp ! =null) {
            callingPid = callerApp.getPid();
            callingUid = callerApp.mInfo.uid;
        } else {
            Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + callingPid
                    + ") when starting: "+ intent.toString()); err = ActivityManager.START_PERMISSION_DENIED; }}/ /... Omit some permission verification code
  
  	// Check whether the current application has permissions, our normal enabled must have permissions
  	booleanabort = ! mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, requestCode, callingPid, callingUid, callingPackage, callingFeatureId, request.ignoreTargetSecurity, inTask ! =null, callerApp, resultRecord, resultStack); abort |= ! mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); abort |= ! mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid, callingPackage);// An Activity corresponds to an ActiityRecord, which is created here
    final ActivityRecord r = newActivityRecord(mService, callerApp, callingPid, callingUid, callingPackage, callingFeatureId, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, request.componentSpecified, voiceSession ! =null, mSupervisor, checkedOptions,
            sourceRecord);
		/ / startActivityUnchecked execution
    mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
            request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,
            restrictedBgActivity, intentGrants);

    return mLastStartActivityResult;
}
Copy the code
ActivityStarter.startActivityUnchecked()
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
ActivityStarter. StartActivityInner () is more important

All startup mode related processing is handled in this method

int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        int startFlags, boolean doResume, ActivityOptions options, Task inTask,
        boolean restrictedBgActivity, NeededUriGrants intentGrants) {
  	// Set the initialization information
    setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
            voiceInteractor, restrictedBgActivity);
		// Determine the startup mode and append the corresponding flags to mLaunchFlags
    computeLaunchingTaskFlags();
		// Get the Activity startup stack
    computeSourceStack();
		// Based on the above calculation, set the flags recognized by the application
    mIntent.setFlags(mLaunchFlags);
	
  	/ /... Omit some activityTask action code

    mTargetStack.startActivityLocked(mStartActivity, topStack.getTopNonFinishingActivity(),
            newTask, mKeepCurTransition, mOptions);
    if (mDoResume) {// After handling the problem of starting the stack task stack, ready to implement the initiator's Resume state
        final ActivityRecord topTaskActivity =
                mStartActivity.getTask().topRunningActivityLocked();
        if(! mTargetStack.isTopActivityFocusable() || (topTaskActivity ! =null&& topTaskActivity.isTaskOverlay() && mStartActivity ! = topTaskActivity)) {// If the activity is not focusable, we can't resume it, but still would like to
            // make sure it becomes visible as it starts (this will also trigger entry
            // animation). An example of this are PIP activities.
            // Also, we don't want to resume activities in a task that currently has an overlay
            // as the starting activity just needs to be in the visible paused state until the
            // over is removed.
            // Passing {@code null} as the start parameter ensures all activities are made
            // visible.
            mTargetStack.ensureActivitiesVisible(null /* starting */.0 /* configChanges */, !PRESERVE_WINDOWS);
            // Go ahead and tell window manager to execute app transition for this activity
            // since the app transition will not be triggered through the resume channel.
            mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
        } else {
            // If the target stack was not previously focusable (previous top running activity
            // on that stack was not visible) then any prior calls to move the stack to the
            // will not update the focused stack. If starting the new activity now allows the
            // task stack to be focusable, then ensure that we now update the focused stack
            // accordingly.
            if(mTargetStack.isTopActivityFocusable() && ! mRootWindowContainer.isTopDisplayFocusedStack(mTargetStack)) { mTargetStack.moveToFront("startActivityInner");
            }
          	Resume our mStartActivity from here
            mRootWindowContainer.resumeFocusedStacksTopActivities(
                    mTargetStack, mStartActivity, mOptions);
        }
    }
    mRootWindowContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);

    // Update the recent tasks list immediately when the activity starts
    mSupervisor.mRecentTasks.add(mStartActivity.getTask());
    mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),
            mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetStack);

    return START_SUCCESS;
}
Copy the code

RootWindowContainer.resumeFocusedStacksTopActivities()

​ =>ActivityStack.resumeTopActivityUncheckedLocked()

. = > ActivityStack resumeTopActivityInnerLocked (), resume the Activity of the final execution in this method

ActivityStack. ResumeTopActivityInnerLocked () is more important
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    
  	/ /...
  	Pausing == true Indicates that Pause is required
    boolean pausing = taskDisplayArea.pauseBackStacks(userLeaving, next);
    / /... Omit the code, and here we do something based on pausing
		/ /...
    if (next.attachedToProcess()) {
      	/ /...
    } else {
        // Whoops, need to restart this activity!
        if(! next.hasBeenLaunched) { next.hasBeenLaunched =true;
        } else {
            if (SHOW_APP_STARTING_PREVIEW) {
                next.showStartingWindow(null /* prev */.false /* newTask */.false /* taskSwich */); }}// Start a specific Activity
        mStackSupervisor.startSpecificActivity(next, true.true);
    }
    return true;
}
Copy the code
ActivityStackSupervisor.startSpecificActivity()
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
    // Is this activity's application already running?
  	// Get the target process
    final WindowProcessController wpc =
            mService.getProcessController(r.processName, r.info.applicationInfo.uid);

    boolean knownToBeDead = false;
    if(wpc ! =null && wpc.hasThread()) {
        try {
          	// If the process exists, proceed to the subsequent process
            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;
    }

    r.notifyUnknownVisibilityLaunchedForKeyguardTransition();

    final boolean isTop = andResume && r.isTopRunningActivity();
  	// Start the process asynchronously if it does not exist
    mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
}
Copy the code

summary

Although the previous process is very complicated, but the summary is to start the Activity of some columns of verification, if the conditions are met into the later fork process or directly start the Activity of the target process

ActivityTaskManagerService.startProcessAsync()

void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
        String hostingType) {
    try {
        if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
                    + activity.processName);
        }
        // Post message to start process to avoid possible deadlock of calling into AMS with the
        // ATMS lock held.
      	/ / sent via handle executive function ActivityManagerInternal: : startProcess
        final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
                mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
                isTop, hostingType, activity.intent.getComponent());
        mH.sendMessage(m);
    } finally{ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); }}Copy the code

ActivityManagerService.LocalService.startProcess()

​ =>ActivityManagerService.startProcess.startProcessLocked()

​ =>ProcessList.startProcessLocked()

ProcessList.startProcessLocked()
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
        boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
        int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
        boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs,
        Runnable crashHandler) {
    long startTime = SystemClock.uptimeMillis();
    ProcessRecord app;

  	//....

    if (app == null) {// Execute here
        checkSlow(startTime, "startProcess: creating new process record");
      	// New ProcessRecord Isolated == false
        app = newProcessRecordLocked(info, processName, isolated, isolatedUid, hostingRecord);
        if (app == null) {
            Slog.w(TAG, "Failed making new process record for "
                    + processName + "/" + info.uid + " isolated=" + isolated);
            return null;
        }
        app.crashHandler = crashHandler;
        app.isolatedEntryPoint = entryPoint;
        app.isolatedEntryPointArgs = entryPointArgs;
        if(precedence ! =null) {
            app.mPrecedence = precedence;
            precedence.mSuccessor = app;
        }
        checkSlow(startTime, "startProcess: done creating new process record");
    } else {
        //....
    }
		/ /...
  	//
    final boolean success =
            startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);
    return success ? app : null;
}
Copy the code
ProcessList.newProcessRecordLocked()
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
        boolean isolated, int isolatedUid, HostingRecord hostingRecord) { String proc = customProcess ! =null ? customProcess : info.processName;
    final int userId = UserHandle.getUserId(info.uid);
    int uid = info.uid;
    if (isolated) {
        / /... Isolated == false Do not execute here
    }
    final ProcessRecord r = new ProcessRecord(mService, info, proc, uid);
		/ /... Omit the initialization ProcessRecord code
  	// Add our ProcessRecord to the mProcessNames collection for easy access
    addProcessNameLocked(r);
    return r;
}
Copy the code
ProcessList.startProcessLocked()
boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
        int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
        String seInfo, String requiredAbi, String instructionSet, String invokeWith,
        long startTime) {
  	/ /... Some initialization of parameters

    if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
      	// Do this asynchronously
        mService.mProcStartHandler.post(() -> handleProcessStart(
                app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,
                requiredAbi, instructionSet, invokeWith, startSeq));
        return true;
    } else {
        try {
            final Process.ProcessStartResult startResult = startProcess(hostingRecord,
                    entryPoint, app,
                    uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
                    requiredAbi, instructionSet, invokeWith, startTime);
            handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
                    startSeq, false);
        } catch (RuntimeException e) {
          / /... Boot failure
        }
        return app.pid > 0; }}Copy the code
ProcessList.startProcess()
private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
        ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
        int mountExternal, String seInfo, String requiredAbi, String instructionSet,
        String invokeWith, long startTime) {
    try {
        / /...
        final Process.ProcessStartResult startResult;
        if (hostingRecord.usesWebviewZygote()) {// Use webView Zygote, the code does not execute here
            startResult = startWebView(entryPoint,
                    app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                    app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                    app.info.dataDir, null, app.info.packageName, app.mDisabledCompatChanges,
                    new String[]{PROC_START_SEQ_IDENT + app.startSeq});
        } else if (hostingRecord.usesAppZygote()) {// Using AppZygote, the code executes here
          	
            final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);

            // We can't isolate app data and storage data as parent zygote already did that.
            startResult = appZygote.getProcess().start(entryPoint,
                    app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                    app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                    app.info.dataDir, null, app.info.packageName,
                    /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
                    app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
                    false.false.new String[]{PROC_START_SEQ_IDENT + app.startSeq});
        } else {// Start the default process
            startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                    app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                    app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                    isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
                    whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                    new String[]{PROC_START_SEQ_IDENT + app.startSeq});
        }
        return startResult;
    } finally {
      	//....}}Copy the code

appZygote.getProcess().start() == ZygoteProcess.start()

= > ZygoteProcess. StartViaZygote () method will be called openZygoteSocketIfNeeded open ZygoteSocket (abi) method

​ =>ZygoteProcess.zygoteSendArgsAndGetResult()

​ =>ZygoteProcess.attemptZygoteSendArgsAndGetResult()

Notify ZygoteServer to start the service through the Socket

ZygoteServer.runSelectLoop()

Handling socket connections

Runnable runSelectLoop(String abiList) {
  	// Socket file descriptor
    ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
  	/ / socket connection
    ArrayList<ZygoteConnection> peers = new ArrayList<>();
    socketFDs.add(mZygoteSocket.getFileDescriptor());
    peers.add(null);
    / /...
    while (true) {
      	StructPollfd[] pollFDs;
      	// Allocate enough space for the poll structs, taking into account
         // the state of the USAP pool for this Zygote (could be a
         // regular Zygote, a WebView Zygote, or an AppZygote).
         if (mUsapPoolEnabled) {
             usapPipeFDs = Zygote.getUsapPipeFDs();
             pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
         } else {
             pollFDs = new StructPollfd[socketFDs.size()];
         }

         /* * For reasons of correctness the USAP pool pipe and event FDs * must be processed before the session and server sockets. This * is to ensure that the USAP pool accounting information is * accurate when handling other requests like API deny list * exemptions. */

         int pollIndex = 0;
         for (FileDescriptor socketFD : socketFDs) {
             pollFDs[pollIndex] = new StructPollfd();
             pollFDs[pollIndex].fd = socketFD;
             pollFDs[pollIndex].events = (short) POLLIN;
             ++pollIndex;
         }

         final int usapPoolEventFDIndex = pollIndex;
        / /...
        int pollReturnValue;
        try {
          	// This will block, waiting for the pollTimeoutMs timeout.
            pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
        } catch (ErrnoException ex) {
            throw new RuntimeException("poll failed", ex);
        }
        if (pollReturnValue == 0) {
          / /... timeout
        } else {
          	// Process all possible FD instructions
            while (--pollIndex >= 0) {
                if ((pollFDs[pollIndex].revents & POLLIN) == 0) {// Indicates that FD has no command to execute
                    continue;
                }

                if (pollIndex == 0) {
                    // Zygote server socket
										// There is a new socket connection instruction to create a connection
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                  	// Add a socket connection
                    peers.add(newPeer);
                  	// Add fd to socketFDs
                    socketFDs.add(newPeer.getFileDescriptor());
                } else if (pollIndex < usapPoolEventFDIndex) {
                    // receive the fork command
                    try {
                        ZygoteConnection connection = peers.get(pollIndex);
                      	// Fork. If command is not empty, command. Run will execute to ActivityThread.main
                        final Runnable command = connection.processOneCommand(this);
												/ / connection. ProcessOneCommand (this) if it is the child will mIsForkChild set to true
                        if (mIsForkChild) {
                            if (command == null) {
                                throw new IllegalStateException("command == null");
                            }
                            return command;
                        } else {
                            if(command ! =null) {
                                throw new IllegalStateException("command ! = null");
                            }
                            if(connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(pollIndex); socketFDs.remove(pollIndex); }}}catch (Exception e) {
                        if(! mIsForkChild) { ZygoteConnection conn = peers.remove(pollIndex); conn.closeSocket(); socketFDs.remove(pollIndex); }else {
                            throwe; }}finally {
                      	/ / reduction mIsForkChild
                        mIsForkChild = false; }}else {
                    / /...}}/ /...
        }
        / /...}}Copy the code
ZygoteConnection.processOneCommand

The place where the process is fork

Runnable processOneCommand(ZygoteServer zygoteServer) {
		//....
    int pid = -1; .//Fork the child process to get a new PID/fork child process, copy on write mode, execute once, return twice/// Pid =0 indicates that Zygote fork is successful
    //pid > 0 indicates the true PID of the child process

    pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
            parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
            parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
            parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
            parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
            parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);

    try {
        if (pid == 0) {
            // in child
            zygoteServer.setForkChild();

            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;

            return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
        } else { / / Zygote process
            // In the parent. A pid < 0 indicates a failure and will be handled in
            // handleParentProc.
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            handleParentProc(pid, serverPipeFd);
            return null; }}finally {
      	/ /...}}Copy the code

ZygoteConnection.handleChildProc()

​ =>ZygoteInit.zygoteInit()

​ =>RuntimeInit.applicationInit()

​ =>RuntimeInit.findStaticMain()

. = > RuntimeInit MethodAndArgsCaller executive ActivityThread () returns. The main () a Runnable

​ =>ActivityThread.main()

​ =>ActivityThread.attach()

​ =>ActivityManagerService.attachApplication()

​ =>ActivityManagerService.attachApplicationLocked()

ActivityManagerService.attachApplication()
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    ProcessRecord app;
		/ /...
  	// the mProcessesReady variable is assigned to true in AMS's systemReady,
    // normalMode is true
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
   	/ /...
    if (normalMode) {
        try {
          	//mAtmInternal == LocalServices.getService(ActivityTaskManagerInternal.class);
          	/ / will eventually perform ActivityTaskManagerService. LocalService. AttachApplication ()
            didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
        } catch (Exception e) {
          	/ /...}}/ /...
    return true;
}
Copy the code

ActivityTaskManagerService.LocalService.attachApplication()

​ =>WindowManagerService.mRoot.attachApplication() == RootWindowContainer.attachApplication()

RootWindowContainer.attachApplication()
boolean attachApplication(WindowProcessController app) throws RemoteException {
    final String processName = app.mName;
    boolean didSomething = false;
    for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
        final DisplayContent display = getChildAt(displayNdx);
        final ActivityStack stack = display.getFocusedStack();
        if (stack == null) {
            continue;
        }

        mTmpRemoteException = null;
        mTmpBoolean = false; // Set to true if an activity was started.
        final PooledFunction c = PooledLambda.obtainFunction(
                RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,
                PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivity());
      	/ / execution WindowContainer. ForAllActivities ()
        stack.forAllActivities(c);
        c.recycle();
        if(mTmpRemoteException ! =null) {
            throw mTmpRemoteException;
        }
        didSomething |= mTmpBoolean;
    }
  	/ /...
    return didSomething;
}
Copy the code

WindowContainer. ForAllActivities () method of the total call RootWindowContainer: : startActivityForAttachedApplicationIfNeeded

RootWindowContainer.startActivityForAttachedApplicationIfNeeded()
private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r, WindowProcessController app, ActivityRecord top) {
    / /...

    try {
      	/ / execution ActivityStackSupervisor. RealStartActivityLocked ()
        if (mStackSupervisor.realStartActivityLocked(r, app, top == r /*andResume*/.true /*checkConfig*/)) {
            mTmpBoolean = true; }}catch (RemoteException e) {
       	/ /...
        return true;
    }
    return false;
}
Copy the code

ActivityStackSupervisor. StartSpecificActivity () method if the process inside also perform ActivityStackSupervisor. RealStartActivityLocked (), So the creation process has come full circle and the process behind it has come together again to actually start the Activity

ActivityStackSupervisor.realStartActivityLocked()

boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
        boolean andResume, boolean checkConfig) throws RemoteException {
		/ /...
    try {
        //....
        try {
						// Create activity launch transaction.
          	/ / create ClientTransaction
            final ClientTransaction clientTransaction = ClientTransaction.obtain(
                    proc.getThread(), r.appToken);

            final DisplayContent dc = r.getDisplay().mDisplayContent;
          	//clientTransaction adds a callback
            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.getSavedState(), r.getPersistentSavedState(), results, newIntents,
                    dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                    r.assistToken, r.createFixedRotationAdjustmentsIfNeeded()));

            // Set desired final state.
            final ActivityLifecycleItem lifecycleItem;
            if (andResume) {
                lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
            } else {
                lifecycleItem = PauseActivityItem.obtain();
            }
            clientTransaction.setLifecycleStateRequest(lifecycleItem);

            // Schedule transaction.
          	// Execute our transaction
            mService.getLifecycleManager().scheduleTransaction(clientTransaction);
						/ /...
        } catch (RemoteException e) {
            //....}}finally {
        / /...
    }
		/ /...
    return true;
}
Copy the code
ClientLifecycleManagerscheduleTransaction()
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
    final IApplicationThread client = transaction.getClient();
  	// start cross-process call
    transaction.schedule();
    if(! (clientinstanceofBinder)) { transaction.recycle(); }}Copy the code
ClientTransaction.schedule()
public void schedule(a) throws RemoteException {
  	//mClient is a proxy for ApplicationThread Binder
    mClient.scheduleTransaction(this);
}
Copy the code

Processing (end)

ApplicationThread.scheduleTransaction()

= > ActivityThread. ScheduleTransaction () = = ClientTransactionHandler. ScheduleTransaction () ActivityThread inheritance ClientTransactionHandler

ClientTransactionHandler.scheduleTransaction()

void scheduleTransaction(ClientTransaction transaction) {
    transaction.preExecute(this);
  	// Switch to the main thread to handle the EXECUTE_TRANSACTION event via Handler
    sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
Copy the code

H.handleMessage()

public void handleMessage(Message msg) {
    switch (msg.what) {
        case EXECUTE_TRANSACTION:
            final ClientTransaction transaction = (ClientTransaction) msg.obj;
        		// The following code is in the main thread
            mTransactionExecutor.execute(transaction);
            if (isSystem()) {
                transaction.recycle();
            }
            break; }}Copy the code

TransactionExecutor.execute()

​ =>TransactionExecutor.executeCallbacks()

TransactionExecutor.executeCallbacks()

public void executeCallbacks(ClientTransaction transaction) {
  	// The callbacks collection here is what we will be executing
    final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
    if (callbacks == null || callbacks.isEmpty()) {
        return;
    }
		/ /...
    final int size = callbacks.size();
    for (int i = 0; i < size; ++i) {
        final ClientTransactionItem item = callbacks.get(i);
      	// You may have some questions about what to do next. . This should return to ActivityStackSupervisor realStartActivityLocked () method we add LaunchActivityItem callback, So the following is executed to LaunchActivityItem. Executeitem.execute(mTransactionHandler, token, mPendingActions); item.postExecute(mTransactionHandler, token, mPendingActions); }}Copy the code

LaunchActivityItem.execute()

public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) {
    ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
            mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
            mPendingResults, mPendingNewIntents, mIsForward,
            mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments);
  	// The client is the ActivityThread, so we get into familiar code
    client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
}
Copy the code

conclusion

Acitivity is a complex startup process that requires a general understanding of how it works so that it doesn’t get confused during these code jumps. Anyway, this is the end of the Activity launch process, and there are many other small details that are not covered too much for space.