preface

Activity.startactivity () is a familiar feature. But it’s like the most familiar stranger, because just the API doesn’t know what’s going on when you start an Activity. That’s the purpose of this article — to clarify what happens when the Activity starts.

But it doesn’t start with activity.startActivity (). Because more often than not, you click on your desktop APP icon and launch your APP. This is actually a “Launcher Activity” that starts an APP, also known as the root Activity launch. The launch of other activities in the same APP is called the launch of child activities.

NOTE: The body of the source version 8.0, the source code is different, but the core remains the same.

Look at activities from the AMS perspective

Before analyzing the startup of an Activity, consider the existence of the Activity from the perspective of AMS(ActivityManagerService). AMS is one of the most core services of Android. Its main function can be understood as managing four components, processing application process and recording system running status. As one of the four components, Activity is naturally subject to the management of AMS. In the eyes of AMS, it does not deal with the Activity directly, but records the running data and state information of the Activity through the media of ActivityRecord.

Knowing an ActivityRecord is not enough. An Activity needs to run in a process. ProcessRecord records all information about the process. By organizing the ActivityRecord and ProcessRecord information, AMS knows how to run an Activity.

From the process of using an APP, it is natural to think that all activities running using the APP should be associated with the APP dimension. In the description of AMS, there is a difference with the idea above. AMS prefers to describe the aggregation of a series of ActivityRecords in terms of tasks.

The ActivityRecord color represents the process.

The activities described in ActivityRecord for Task aggregation can come from different processes, so tasks can be composed of different apps to perform a series of tasks.

The sample is as follows

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".FActivity"
            android:launchMode="singleInstance"></activity>
        <activity android:name=".TActivity" />
        <activity
            android:name=".SActivity"
            android:process=":remote" />
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
Copy the code

In the figure, the Activity is started from Main -> S -> T -> F, by command

adb shell dumpsys activity | grep packageName

You can see how the current Activity looks in AMS, as shown in the first figure.

  • Main, S, and T belong to TASK #5791, and F belongs to another TASK #5792
  • Main, T, F run on process 30223, S run on process 30670

Root start

Back to business.

The most common scenario for root startup is to click an APP icon on the desktop to Launch the “Launch Activity” of the APP. In this scenario, the desktop is actually a Launcher, and a Launcher is also an Activity that aggregates information about other apps for the convenience of users. If your phone isn’t using a Launcher, the screen looks like a terminal when you start it up.

Since Launcher is an Activity, it follows the Activity life cycle, as shown in launcher.oncreate ()

@Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        if(! mRestoring) {if (sPausedFromUserAction) {
                mModel.startLoader(true, 1); }else{// Start to load app information, initial state sPausedFromUserAction isfalse
                mModel.startLoader(true, mWorkspace.getCurrentPage()); }}... }Copy the code

MModel by LauncherModel open task thread LoaderTask to PMS for each APP launch information, information is kept in the LauncherModel. MBgAllAppsList. Collect key information, such as the App package name defined in each Manifest, information about activities defined as action MAIN and category LAUNCHER, and application ICONS. Associate information and generate desktop ICONS.

If you are interested in the above process, check out -> luancher.startLoader () -> loaderTask.run () -> loadAndBindAllApps() -> loadAllAppsByBatch(). Don’t go deep.

When the Launcher is ready and the desktop generates the corresponding icon, click on an icon to start the root Activity of an APP.

Launcher

    public void onClick(View v) {
        ......
        Object tag = v.getTag();
        if(tag instanceof ShortcutInfo) { ...... boolean success = startActivitySafely(v, intent, tag); . }... }Copy the code

The APP map punctuation solution is received by launcher.onclick (), and the previously associated key launch information is stored in the View tag, which is retrieved here and used to launch. Link to the Launcher. StartActivitySafely () – > the Launcher. StartActivity () – > Activity.startactivity (), which sets the FLAG_ACTIVITY_NEW_TASK flag bit for the Intent used to launch it. It is not difficult to understand that there is no TASK related to this APP in AMS, so it is natural to create a new TASK for this startup. The Launcher itself is an Activity and finally starts with activity.startActivity ().

In the Activity. StartActivity () started, no matter what form, finally arrived at the Activity. StartActivityForResult (), as follows

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @nullable Bundle options) {// In root startup, there is no mParentif(mParent == null) { ...... options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); . } // The child Activity startselse {
            if(options ! = null) { mParent.startActivityFromChild(this, intent, requestCode, options); }else {
                // Note we want to go through this method forcompatibility with // existing applications that may have overridden it. mParent.startActivityFromChild(this, intent, requestCode); }}}Copy the code

The mInstrumentation is Instrumentation, which monitors the interaction between the application and the system. The mMainThread is an ActivityThread, but the ActivityThread is not an ActivityThread. The Main thread is an ActivityThread. The Main thread is an ActivityThread. For example, the main thread Looper is created, the ApplicationThread communicates with AMS, and so on. With components like this, you can do a lot of things that are done in the main thread to make an ActivityThread look like the main thread.

When the system starts an application process, an ActivityThread is loaded into the process and is held by the parent Activity of the Activity component that the process starts.

Activity.mToken is activityRecord. Token. As mentioned earlier, ActivityRecord is responsible for maintaining Activity state information. In the current scenario, with the ActivityRecord for the Launcher, AMS naturally knows the details of the Launcher for subsequent operations.

Back in the Instrumentation. ExecStarActivity ()

    public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread; Uri referrer = target ! = null ? target.onProvideReferrer() : null;if(referrer ! = null) { intent.putExtra(Intent.EXTRA_REFERRER, referrer); } // Try using ActivityMonitor to intercept the start of the Activity and handle some of your own transactions, mainly for testing purposesif(mActivityMonitors ! = null) { ...... } / / by AMS launched the Activity the try {intent. MigrateExtraStreamToClipData (); intent.prepareToLeaveProcess(who); int result = ActivityManager.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;
    }
Copy the code

Instrumentation hands off the startup operation to AMS. The retrieval of AMS by ActivityManager. GetService () through the android. Utils, Singleton and ServiceManager access to AMS IAcitivityManager proxy objects. This operation involves IPC.

Through AMS. StartActivity () to reach ActivityStarter. StartActivityMayWait (), Path for AMS. StartActivity () – > AMS. StartActivityAsUser () – > ActivityStarter. StartActivityMayWait ()

    final int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult,
            Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
            IActivityContainer iContainer, TaskRecord inTask, String reason) {// resultTo is activity. mToken, the intent contains information about the Activity to be launched...... / / with reference to intent, collecting the Manifest information from PMS ResolveInfo rInfo = mSupervisor. ResolveIntent (intent, resolvedType, userId); . // Collect information about the target of the intent. ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo); ActivityOptions options = ActivityOptions.fromBundle(bOptions); ActivityStackSupervisor.ActivityContainer container = (ActivityStackSupervisor.ActivityContainer)iContainer; synchronized (mService) { ...... final ActivityStack stack;if(container = = null | | container. MStack. IsOnHomeDisplay ()) {/ / root Activity can come here, Before the container is empty from the stack = mSupervisor. MFocusedStack; }else {
                stack = container.mStack;
            }
            

            if(aInfo ! = null && (aInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) ! = 0) {// Prevent the same process already startedif(aInfo.processName.equals(aInfo.applicationInfo.packageName)) { ...... } // This ActivityRecord will be used to record the newly enabled Activity final ActivityRecord[] outRecord = new ActivityRecord[1]; int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
                    aInfo, rInfo, voiceSession, voiceInteractor,
                    resultTo, resultWho, requestCode, callingPid,
                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                    options, ignoreTargetSecurity, componentSpecified, outRecord, container,
                    inTask, reason); . }Copy the code

First, the PMS collects Manifest information from the intent and stores it in ResolveInfo, which contains information about services, activities, and providers. With ResolveInfo comes ActivityInfo — more information about the Activity to be launched, including theme, launchMode, taskAffinity, and more.

Once you have this information, you need to verify that the same process is already running. You can also see that the process name is the same as the package name. In the current case, there is no corresponding process. OutRecord will be the ActivityRecord that records the status of the Activity to be launched later.

Link – > ActivityStarter. StartActivityLocked () – > ActivityStarter. StartActivity ()

    /** DO NOT call this method directly. Use {@link #startActivityLocked} instead. */
    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,
            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
            ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
            TaskRecord inTask) { int err = ActivityManager.START_SUCCESS; // Pull the optional Ephemeral Installer-only bundle out of the options early. final Bundle verificationBundle = options ! = null ? options.popAppVerificationBundle() : null; ProcessRecordcallerApp = null;
        if (caller! = null) {// getcallerProcessRecord of the process in which you are runningcallerApp = mService.getRecordForAppLocked(caller);
            if (callerApp ! = null) {// get pid and uid callingPid =callerApp.pid;
                callingUid = callerApp.info.uid;
            } else {
                Slog.w(TAG, "Unable to find app for caller " + caller
                        + " (pid=" + callingPid + ") when starting: "+ intent.toString()); err = ActivityManager.START_PERMISSION_DENIED; }}... ActivityRecordsourceRecord = null; // Source Activity ActivityRecord resultRecord = null;if(resultTo ! = null) {// Get an existing one from the Activity StacksourceRecord, the root starts the AR corresponding to the launchersourceRecord = mSupervisor.isInAnyStackLocked(resultTo); . }... AR ActivityRecord r = new ActivityRecord(mService,callerApp, callingPid, callingUid, callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, componentSpecified, voiceSession ! = null, mSupervisor, container, options,sourceRecord);
        if(outActivity ! = null) { outActivity[0] = r; }...return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,
                options, inTask, outActivity);
Copy the code

ProcessRecord is used to describe a process, and mProcessNames inside AMS stores information about existing processes. In the current scenario, you get the ProcessRecord of the process Launcher, record the relevant PID and UID, and get the ActivityRecord of the Launcher from the Stack. Next, create an ActivityRecord for the Activity you want to start to manage and maintain it later.

Link ActivityStarter. StartActivity () – > ActivityStarter. StartActivityUnchecked ()

    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, ActivityRecord[] outActivity) {/** * Because the ActivityStarter is used to start the Activity, the necessary data is already there. Associate the data with the ActivityStarter for subsequent processing */setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession, voiceInteractor); // Check the flag bit to determine whether to start a new Task, i.e., whether to create a new Stack, satisfying the condition mAddingToTask =truecomputeLaunchingTaskFlags(); computeSourceStack(); mIntent.setFlags(mLaunchFlags); /** * Consider the newly enabled Activity singleTask or singleInstance startup mode, At this point may already have AR and the corresponding Task * / ActivityRecord reusedActivity = getReusableIntentActivity (); . // If this condition is met, the root node does not need to know the execution resultif(mStartActivity.resultTo == null && mInTask == null && ! mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) ! = 0) { newTask =true; // Make sure to get available Task result =setTaskFromReuseOrCreateNewTask( taskToAffiliate, preferredLaunchStackId, topStack); }... /** * this function puts the Activity at the top of the stack, In order to activate after * / mTargetStack. StartActivityLocked (mStartActivity topFocused, newTask, mKeepCurTransition, mOptions);if (mDoResume) {
             mWindowManager.executeAppTransition();
            } else {
                if(mTargetStack.isFocusable() && ! mSupervisor.isFocusedStack(mTargetStack)) { mTargetStack.moveToFront("startActivityUnchecked"); } / / root to start here mSupervisor. ResumeFocusedStackTopActivityLocked (mTargetStack mStartActivity, mOptions); }}Copy the code

Before you actually start the Activity, record some key information stored in the ActivityStarter member variable, which can be accessed directly later. In addition, you need to consider the startup mode of an Activity. Usually, one Activity starts another Activity, and the two activities are clustered together in the same Task. But when you set the flag to FLAG_ACTIVITY_NEW_TASK, you look for the appropriate Task for the Activity to start. The word “appropriate” indicates that the Task is reusable. If no reusable Task is found, create a new Task.

As mentioned earlier, the root Activity from the Launcher is already set to FLAG_ACTIVITY_NEW_TASK, so a new Task is created to process it. The Activity to be started is then placed at the top of the stack for later activation.

In ActivityStack, MResumeActivity, mLastPausedActivity, and mPausingActivity are used to record the current Activity to be activated, the Activity paused last time, and the Activity paused this time, respectively.

Link ActivityStackSupervisor. ResumeFocusedStackTopActivityLocked () – > ActivityStack. ResumeTopActivityUncheckedLocked () – > ActivityStack.resumeTopActivityInnerLocked()

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { ...... // Get the AR Final ActivityRecord next = topRunningActivityLocked(true/* focusableOnly */); . If the RESUMED Activity you are about to activate is mResumedActivity and is in a RESUMED state, do nothingif(mResumedActivity == next && next.state == ActivityState.RESUMED && ...... / / whether or not to notify source Activity pause Boolean pausing. = mStackSupervisor pauseBackStacks (userLeaving, next,false);
        if(mResumedActivity ! = null) {if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing "+ mResumedActivity); / / to inform the Launcher, it enters the pause state pausing | = startPausingLocked (userLeaving,false, next, false);
        }
            return false; }... // Root will not enter here when it starts because next. App is emptyif(next.app ! = null && next.app.thread ! = null) { ......if(next.newIntents ! = null) {/ / start the activity next. The app. The thread. ScheduleNewIntent (next) newIntents, next appToken,false/* andPause */); }}else{... / / root node starts, for the activity start process mStackSupervisor. StartSpecificActivityLocked (next,true.true); }... }Copy the code

The ActivityRecord for previously active activities is pushed to the top of the stack and can be retrieved directly from the top. At this point, you will delegate an ActivityThread to start the Activity. However, the process required for the root Activity has not been created, so there is no corresponding ActivityThread. You need to create the process first and then activate the Activity. Before creating a process, notify the Launcher to enter the pause state.

ActivityThread

An ActivityThread is created with the creation of an APP process. In the ActivityRecord case, ActivityRecord knows all sorts of information about the Activity when it is running, including ProcessRecord, which describes the process in which it is running. ProcessRecord has an instance of IApplicationThread, and when AMS needs to manipulate an Activity, The native Binder that requires ApplicationThread, IApplicationThread, communicates with the APP process.

In ActivityThread, a Handler of class H receives various message signals to process the corresponding events.

The Launcher to pause

ActivityStack.startPausingLocked()

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming, boolean pauseImmediately) { ...... // Activity ActivityRecord prev = mResumedActivity; . // Update, because mLastPausedActivity represents the Activity that was paused last time mLastPausedActivity = prev; .if(prev.app ! = null && prev.app.thread ! = null) {if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
            try {
                EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                        prev.userId, System.identityHashCode(prev),
                        prev.shortComponentName);
                mService.updateUsageStats(prev, false); // Send a notification to the AT to start the source Activity pause, Have the opportunity to perform your own bussiness prev. App. Thread. SchedulePauseActivity (prev. AppToken, prev. Finishing, userLeaving, prev.configChangeFlags, pauseImmediately); }... }...if(mPausingActivity ! = null) { ......else{// Issue PAUSE_TIMEOUT_MSG schedulePauseTimeout(prev);return true; }}}Copy the code

Use IApplicationThread to make the ActivityThread handle the Pause event of the Launcher, and make the Launcher time handle its Pause() logic. At the same time, after PAUSE_TIMEOUT_MSG is delayed, Send a message to activate a new Activity. The reason for this is that AMS needs to start a new Activity after ensuring that the onPause() event of the old Activity completes, which is consistent with what we know about the Activity lifecycle. Since it is an asynchronous interprocess operation, ActivityStack and AMS do not know exactly when the old Activity completes onPause(), so AMS decides to wait for PAUSE_TIMEOUT_MSG, If the old Activity does not complete onPause() within PAUSE_TIMEOUT_MSG, it is not considered relevant.

The PAUSE_TIMEOUT_MSG event is received by the ActivityStack inner class ActivityStackHandler

Link ActivityStack. ActivityPausedLocked () – > ActivityStack.com pletePauseLocked () – > ActivityStackSupervisor.resumeFocusedStackTopActivityLocked() -> ActivityStackSupervisor.resumeTopActivityUncheckedLocked() -> ActivityStack.resumeTopActivityInnerLocked()

Once again come to ActivityStack resumeTopActivityInnerLocked ()

The first time it comes, it gives a Pause to the old Activity and the second time it comes, it starts the new Activity

Previously, the start of the root Activity, the first time it arrived, also required the creation of a new process for the APP

 private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
     ......
     mStackSupervisor.startSpecificActivityLocked(next, true.true); . }Copy the code

ActivityStackSupervisor.startSpecificActivityLocked()

Void startSpecificActivityLocked (ActivityRecord r, Boolean andResume, Boolean checkConfig) {/ * * * in the AMS, Each Activity component has a UID and process name, which is assigned by the PMS when the Activity is installed. * The process name is determined by the Android: Process attribute. When AMS starts an Activity, it first checks whether the * process exists by its UID and process name. Otherwise new process restart. * / ProcessRecord app = mService getProcessRecordLocked (r.p rocessName, r.i show nfo. ApplicationInfo. The uid,true); r.getStack().setLaunchTime(r); // The child Activity runs in the same process as the root Activity. App and app.thread are no longer emptyif(app ! = null && app.thread ! = null) { try {if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) { app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode, mService.mProcessStats); } // The child Activity starts realStartActivityLocked(r, app, andResume, checkConfig) in the same process;return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "+ r.intent.getComponent().flattenToShortString(), e); }} / / cold start mService. StartProcessLocked (r.p rocessName, r.i show nfo. ApplicationInfo,true, 0,
                "activity", r.intent.getComponent(), false.false.true);
    }
Copy the code

Because there is no required process, the process needs to be recreated, also known as the cold start process.

Link – > ActivityManagerService. StartProcessLocked () – > ActivityManagerService. StartProcessLocked () – > ActivityManagerService.startProcessLocked()

    private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
        long startTime = SystemClock.elapsedRealtime();
        if(app.pid > 0 && app.pid ! = MY_PID) { ...... synchronized (mPidsSelfLocked) { mPidsSelfLocked.remove(app.pid); // Send PROC_START_TIMEOUT_MSG mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); }... }... try{ ......else{// Start a new process // entryPoint is"android.app.ActivityThread"Start (entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, invokeWith, entryPointArgs); }... synchronized (mPidsSelfLocked) { this.mPidsSelfLocked.put(startResult.pid, app);if(isActivityProcess) {// If the process cannot start at the PROC_START_TIMEOUT_MSG event, Message MSG = mhandler. obtainMessage(PROC_START_TIMEOUT_MSG); msg.obj = app; mHandler.sendMessageDelayed(msg, startResult.usingWrapper ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT); }}}...Copy the code

Zygote incubates the process when it starts, creating the process as well as the ActivityThread. The process creation must be completed within the PROC_START_TIMEOUT_MSG time, otherwise the creation is considered to have failed and there is no further process.

Activitythreads are created in activityThread.main (), followed by Looper for message processing and ApplicationThread for communication. I’ve passed this one over.

Start the Activity

Going back to the previous process, after the old Activity has been paused, Has can start the Activity ActivityStackSupervisor. ResumeTopActivityInnerLocked () – > ActivityStackSupervisor.startSpecificActivityLocked() -> ActivityStackSupervisor.realStartActivityLocked()

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { ...... ProcessRecord r.appp = app; app.waitingToKill = null; r.launchCount++; r.lastLaunchTime = SystemClock.uptimeMillis();if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);

        int idx = app.activities.indexOf(r);
        if(idx < 0) {// Add the activity to the activity component list app.activities.add(r); }... Try {/ / call ActivityThread scheduleLaunchActivity start-up activity app. Thread. ScheduleLaunchActivity (new Intent (r.i ntent), r.appToken, 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, app.repProcState, r.icicle, r.persistentState, results, newIntents, ! andResume, mService.isNextTransitionForward(), profilerInfo); }... }Copy the code

The relevant ProcessRecord information is logged for the ActivityRecord, and the Activity is started with the ActivityThread

ActivityThread.H.handlerMessage

final ActivityClientRecord r = (ActivityClientRecord) msg.obj; / / need to load the APK, in order to access resources r.p ackageInfo = getPackageInfoNoCheck (state Richard armitage ctivityInfo. ApplicationInfo, r.com patInfo); // Load Activity handleLaunchActivity(r, null,"LAUNCH_ACTIVITY");
Copy the code

Link ActivityThread. HandleLaunchActivity () ActivityThread. PerformLaunchActivity ()

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {/ / / / get the Context object ContextImpl appContext = createBaseContextForActivity (r); Activity activity = null; Try {/ / merits class loader created Activity. Java lang. This 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 {}} / / get the Application, not create Application app = r.p ackageInfo. MakeApplication (false, mInstrumentation);

            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());

            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; } / / create the Activity for the Context appContext. SetOuterContext (Activity); Attach (appContext, this, getInstrumentation(), r.toy, R.i.dent, app, R.I.ntent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback); . int theme = r.activityInfo.getThemeResource(); // Set the activity themeif(theme ! = 0) { activity.setTheme(theme); } activity.mCalled =false; / / call the Activity. The onCreate ()if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else{ mInstrumentation.callActivityOnCreate(activity, r.state); }... }Copy the code

Activity.oncreate () completes, which means the Activity has started.

The child Activity starts

The previous section described the start of the root Activity, while the start of the child Activity is roughly the same, with the following differences

  • In ActivityStarter. StartActivityUnchecked () when looking for a Task for the Activity, has been a Task without having to create a new Task, unless the child Activity set the startup FLAG to FLAG_ACTIVITY_NEW_TASK
  • Activity in the process of different startup, AMS will not create a new Task for child Activity, but will be in ActivityStackSupervisor. StartSpecificActivityLocked () for this Activity to create new processes.

conclusion

  • A Task is a more advanced and abstract concept than a process, and each Activity runs in a Taks. Task brings together a series of related Activity components to perform a business function. For example, if there is A function A application process in the system, including activities that implement function A, when other apps use this function, the Task ensures that the Activity of A is running in the same Task, so that users do not feel the difference between the application process.
  • When starting an Activity, consider the Task and process it is running in
  • AMS needs to ensure that the creation timing of a process and the execution timing of an Activity’s life cycle are controllable
  • The startup process of Activity is actually a communication management process in which AMS uses ActivityRecord as the information medium and IApplicationThread as the communication means

Small thinking

Why not do time-consuming tasks in onPause()? AMS and ActivityThreads ensure some timing and constraint, but have limitations, for the execution of life cycles between old and new activities. The onPause() time-consuming task is considered to affect the new Activity and bring about poor user experience. However, user experience is only part of the problem. More importantly, onPasuse() is an important and reliable time for data persistence, and onStop() and onDestroy() are not guaranteed to be executed when a process dies due to an unexpected event such as a memory restart. User experience alone is not enough. You can experiment with A scenario where A starts B, B is in A different process, and A’s onPause() performs A time-consuming task. In this scenario, B’s onCreate(), onStart(), and onResume() are much earlier than A’s onPause(). Is it possible to perform time-consuming tasks in onPause() in such a scenario?


reference

UserHandle

Task

The process of starting an Activity with Android Launcher

OnPause () is special