Note: The source version analyzed in this article is Android 27

If reproduced, please indicate the source

(Update at 2021-6-11)

First, application startup

First, we need to know:

The Main method of ActivityThread is the entry point for Android application startup.

public final class ActivityThread {
    // Omit some code

    public static void main(String[] args) {
        // Omit some code

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited"); }}Copy the code

The main method does the following:

  • 1, called firstLooper.prepareMainLooper()Method, which creates a Looper object associated with the current thread (main thread).
  • 2. Then create oneActivityThreadObject and call itattach()Methods.
  • 3. Last callLooper.loop()Method to loop the newly created Looper object from its MessageQueue and execute it.

Now, what does the Attach method of the ActivityThread do?

public final class ActivityThread {
    final ApplicationThread mAppThread = new ApplicationThread();
    // Omit some code

    private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if(! system) {// Omit some code

            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            // omit subsequent code}}}Copy the code

In the attach approach, called ActivityManager. GetService () method, get to the remote IActivityManager object, and a ApplicationThread instance into the.

ApplicationThread inherits from iApplicationThread. Stub, the native implementation class for communication between Binder processes. It has a number of Activity lifecycle related methods, most of which are named scheduleXXX:

At this point, we can summarize:

  1. ActivityThreadthemainMethod, which is the entry point to the application launch.
  2. The Activity lifecycle is controlled by the underlying system, throughBinderMechanism, callback toApplicationThreadthescheduleXXXMethods.
  3. ActivityThreadandApplicationThreadFor each application, there is only one instance.

Here’s a question:

Why is there no scheduleStartActivity method? Isn’t the Activity’s onStart() lifecycle callback controlled by ActivityManager sending messages?

With that in mind, we started reading the source code for these scheduleXXX methods.

2. Activity lifecycle

We start our analysis with the scheduleLaunchActivity method in ApplicationThread, because the name implies that it should perform Activity creation and startup.

public final class ActivityThread {
    // Omit some code

    private class ApplicationThread extends IApplicationThread.Stub {
        // Omit some code

        @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, 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 notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            // Assign an ActivityClientRecord value

            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
        }
        // Omit some code}}Copy the code

In the scheduleLaunchActivity method, an ActivityClientRecord object is first created and assigned.

ActivityClientRecord: Records activity-related data.

The thread is then switched from the Binder thread to the main thread by a Handler. Finally, the handleLaunchActivity method of the ActivityThread is called.

public final class ActivityThread {
    // Omit some code

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        // Omit some code

        Activity a = performLaunchActivity(r, customIntent);

        if(a ! =null) {
            // Omit some code
            handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed, r.lastProcessedSeq, reason);if(! r.activity.mFinished && r.startsNotResumed) {// The activity manager actually wants this one to start out paused, because it
                // needs to be visible but isn't in the foreground. We accomplish this by going
                // through the normal startup (because activities expect to go through onResume()
                // the first time they run, before their window is displayed), and then pausing it.
                // However, in this case we do -not- need to do the full pause cycle (of freezing
                // and such) because the activity manager assumes it can just retain the current
                // state it has.
                performPauseActivityIfNeeded(r, reason);
                // Omit some code}}}}Copy the code

For the moment, let’s ignore what’s done in the FormLaunchActivity method and just look at the subsequent code. The next code calls handleResumeActivity, assuming it should call the Activity’s onResume method.

Based on the Activity lifecycle:

In the performLaunchActivity method, the onCreate and onStart methods of the Activity must be called in sequence.

With that in mind, start analyzing the FormLaunchActivity method.

public final class ActivityThread {
    // Omit some code

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // Omit some code

        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            // Omit some code
        } catch (Exception e) { /* omit some code */ }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            // Omit some code

            if(activity ! =null) {
                // Omit some code

                Window window = null;
                if(r.mPendingRemoveWindow ! =null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
                // Omit some code

                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                // Omit some code

                if(! r.activity.mFinished) { activity.performStart(); r.stopped =false;
                }
                if(! r.activity.mFinished) { activity.mCalled =false;
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                    }
                    // Omit some code
                }
            }
            r.paused = true;

            / / save ActivityClientRecord
            mActivities.put(r.token, r);

        } catch  { /* omit catch code */ }

        return activity;
    }
Copy the code

The above code does the following:

  1. Creating an Activity object

Call the newActivity method of Instrumentation to create an Activity object through reflection.

  1. Initialize the Activity

The attach method of the Activity object is called, which initializes some of the Activity’s data and sets the Window object for the Activity. Note: The Window object of the Activity is not the same object as the Window object passed in. This also means that each Activity has its own Window object.

public class Activity extends.{
    // Omit some code
    private Window mWindow;
    private WindowManager mWindowManager;

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        // Omit some codemWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0);
        // Omit some code

        mWindowManager = mWindow.getWindowManager();
        // Omit some code
    }
    // Omit some code
}
Copy the code
  1. Call three lifecycle methods

1, Instrumentation. CallActivityOnCreate method, this method is invoked in the activity. The performCreate () method. 2, activity. PerformStart () method. 3, Instrumentation. CallActivityOnPostCreate method, this method will be called in the activity. The onPostCreate () method.

Call onCreate, onStart, and onPostCreate in order to verify performLaunchActivity.

To sum up:

In the handleLaunchActivity method, the following lifecycle is called back:

onCreate() -> onStart() -> onPostCreate() -> onResume()

Note: if ActivityClientRecord startsNotResumed = true, life cycle process will become:

onCreate() -> onStart() -> onPostCreate() -> onResume() -> onPause()


Two, interface loading

From the previous section, we learned that the handleLaunchActivity method calls back to the Activity’s onCreate() lifecycle method.

Normally, when creating an Activity, we call setContentView in onCreate() to set the layout file.

Let’s start by analyzing how our custom layout file is loaded.

2.1. Set the layout

First analyze the source code of the setContentView method in the Activity.

public class Activity extends.{
    // Omit some code

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
    // Omit some code
}
Copy the code

As you can see from the previous analysis, the getWindow() method here returns the Activity’s unique Window object, which is assigned in the Attach method.

Window is an abstract class, and as you can see from the annotations on the class, it only has a subclass called PhoneWindow, so let’s look directly at PhoneWindow’s setContentView method.

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    // Omit some code
    ViewGroup mContentParent;

    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if(! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); }if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if(cb ! =null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
    // Omit some code
}
Copy the code

If mContentParent == null, call the installDecor() method and then load the layout resources passed in to mContentParent.

So it’s safe to assume that the installDecor() method is used to create mContentParent objects.

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    // Omit some code
    private DecorView mDecor;
    ViewGroup mContentParent;

    private void installDecor(a) {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            // Omit some code
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            // omit the code for setting icon, title, etc}}}Copy the code

Well, the mContentParent object is created using the generateLayout method, but before that, a DecorView object is created and passed in as a parameter to the generateLayout method.

In the DecorView, all we need to know is that it inherits to FrameLayout, because it’s not very helpful to analyze its details at this point.

So let’s analyze what generateLayout did:

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    // Omit some code
    public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

    protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        // Omit some code: read the content from the topic file and set the corresponding Flag

        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        // omit some code: use features to set different layoutResource ids for layoutResource

        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        // Omit some code: Set background, elevation, title, titleColor data for mDecor

        mDecor.finishChanging();

        returncontentParent; }}Copy the code

The main contents are as follows:

  1. Gets data from the topic and applies it to the currentWindowIn the.
  2. According to Features, letmDecorLoad different layout files.
  3. To obtainmContentParentObject (id iscom.android.internal.R.id.content).

Here we can get the following information:

  1. Different themes can makeWindowLoad different layouts intoDecorViewIn the.
  2. setContentViewMethod is actually to load a custom layout file intomContentParentIn the.

At this point, we can briefly summarize the flow of setContentView:

1. First, the Window object in the Activity creates a DecorView. 2. Then let the DecorView load different layout resources based on the theme. MContentParent in 3, obtain the layout resource that id for the com. Android. Internal. R.i, dc ontent. 4. Finally load the custom layout into mContentParent.

2.2. Render layout

In the setContentView process, all the layout resources have been loaded, and the loading of the layout involves the addView method.

In general, the addView method calls requestLayout() and invalidate(true) indirectly, causing the interface to be reconfigured and refreshed.

And we know this:

When the Activity is onCreate, the interface is not loaded.

There seems to be a contradiction here, so let’s analyze it and see why the interface is not loaded when the addView method is called.

public abstract class ViewGroup extends View implements ViewParent.ViewManager {
    // Omit some code

    public void addView(View child, int index, LayoutParams params) {
        // Omit some code
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }
    // Omit some code
}
Copy the code
public class View implements.{
    // Omit some code

    public void requestLayout(a) {
        // Omit some code
        if(mParent ! =null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
        // Omit some code
    }

    public void invalidate(boolean invalidateCache) {
        invalidateInternal(0.0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }

    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
            boolean fullInvalidate) {
        // Omit some code
        if (skipInvalidate()) {
            return;
        }
        // omit subsequent code
    }

    private boolean skipInvalidate(a) {
        return(mViewFlags & VISIBILITY_MASK) ! = VISIBLE && mCurrentAnimation ==null&& (! (mParentinstanceofViewGroup) || ! ((ViewGroup) mParent).isViewTransitioning(this));
    }
    // Omit some code
}
Copy the code

Oh, it turns out that the DecorView doesn’t have a parent at this point, so instead of rearranging and refreshing, only add operations are performed.


When will the interface be loaded?

As anyone who has studied the Android life cycle knows:

When the Activity is in onCreate, it is not visible. When the Activity is onStart, it is visible but not interactive. An Activity is visible and interactive only when it is in onResume.

Let’s look at the implementation of the Activity’s performStart method:

public class Activity extends.{
    // Omit some code

    final void performStart(a) {
        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
        mFragments.noteStateNotSaved();
        mCalled = false;
        mFragments.execPendingActions();
        mInstrumentation.callActivityOnStart(this);
        if(! mCalled) {throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onStart()");
        }
        mFragments.dispatchStart();
        mFragments.reportLoaderStart();

       // Omit some code

        mActivityTransitionState.enterReady(this);
    }
Copy the code

Here as long as the call To the Instrumentation callActivityOnStart method, and the internal implementation of the callActivityOnStart method, simply call the onStart() method of the incoming Activity object.

emmmmm…… There is something wrong with the performStart method. The render interface is in onResume().

With that in mind, let’s take a look at the handleResumeActivity method:

public final class ActivityThread {
    // Omit some code

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        // Omit some code

        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);

        if(r ! =null) {
            final Activity a = r.activity;
            // Omit some code

            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                // Omit some code

                if (a.mVisibleFromClient) {
                    if(! a.mWindowAdded) { a.mWindowAdded =true;
                        wm.addView(decor, l);
                    } else {
                        // The activity will get a callback for this {@link LayoutParams} change
                        // earlier. However, at that time the decor will not be set (this is set
                        // in this method), so no action will be taken. This call ensures the
                        // callback occurs with the decor set.a.onWindowAttributesChanged(l); }}// omit subsequent code}}}}Copy the code

PerformResumeActivity does nothing but call the Activity’s performResume() method, which calls onResume indirectly.

What’s really important here is adding a DecorView to the ViewManager, which indirectly calls the Windows ManagerGlobal addView method, which is a singleton object. Notice the criteria here, as you can see, An ActivityClientRecord object, then executed once.

public final class WindowManagerGlobal {
    // Omit some code
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();

    public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
        // Omit some code

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Omit some code

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throwe; }}}// Omit some code
}
Copy the code

The ViewRootImpl object is created and cached in an array. DecorView into ViewRootImpl and execute requestLayout() -> scheduleTraversals() -> doTraversal() on ViewRootImpl. This is eventually executed in the performTraversals() method.

PerformTraversals () will perform the View rendering process, including measurement, placement and rendering, which will be discussed separately in the View rendering section later. We just need to understand that the interface has already been rendered.

2.3, extending

Now that we know that the Activity renders to the screen after onResume(), why is onStart() visible but not interactive? I won’t keep you in suspense. In fact, the official term “visible” has some ambiguity. I think:

“Visible” only applies to the rendered Activity, not the Activity being created.

Take a look at this picture to illustrate:

  • 1. For the Activity created, it simply goes through its life cycle and renders inResumedFinished in time.
  • 2. For rendered activities:
  1. When it is composed ofResumedState switch toStartedState, where the interface is partially overwritten and out of focus, i.e. unable to interact.
  2. When it is composed ofStartedState switch toCreatedState, the interface is completely overwritten, that is, invisible.
  3. When it is composed ofCreatedState switch toStartedState, the interface is partially covered again, still can not get focus, can not interact.
  4. When it is composed ofStartedState switch toResumedStatus. The interface is fully displayed.

It is this that causes the following problem:

During Activity creation, the onCreate(), onStart(), and onResume() methods cannot get the width and height of the control.

Third, summary

This article explains the life cycle of the call and interface loading process when the Activity starts. In summary, the whole process is as follows: