In a long time ago, the creator of a simple learning Activity structure, but at that time for various reasons, just a taste, and did not carry out in-depth learning. At the same time, I found myself a little decadent after graduation this year, no longer have the same energy of study as last year. After many times of introspection, I found myself decadent because I could not find the direction of study.

After deep self-reflection, in order to change the current situation, to make up for the regret of learning Activity at the beginning, and to improve my own technical level, I decided to start with Activity and learn and record the knowledge of Android Framework.

This article intends to introduce and analyze the structure design of Activity, including: Activity, Window and View related knowledge. References for this article:

  1. An article is enough to understand activities, views, and Windows
  2. The design and implementation of reflection | Android LayoutInflater mechanism
  3. What happens between Activity creation and View rendering?

1. Structure introduction

As you probably know, the Activity structure is divided into three layers: Activity, Window, and View. Each layer has different responsibilities.

Activity

  1. ActivityThread: The starting point of each process invocation, asActivityThreadContent is not the focus of this article, so this article will not be introduced too much, there will be related articles to introduce.
  2. Activity: Acts like a manager, responsible for creatingWindowManagerandWindow, while initializationView.
  3. Windows: bearingViewThe contemporaryActivityDeal with allViewThe transaction.
  4. WindowManager: LiterallyWindowManagement, in fact, is managementWindowOn theView, includingaddViewandremove.

ActivityThread is globally unique and has only one instance in the App process. An ActivityThread can correspond to multiple activities, one Activity to a Window(not a Dialog, for example), and one Window to a WindowManager.

This article intends to refer to the above flow chart to analyze the Attach process of Activity, the onCreateView process of Activity and the addView process of WindowManager respectively.

2. The Activity of the attach

The attach process of the Activity is an initialization process, which initializes the Window and WindowManager respectively. Let’s start by looking at the handleLaunchActivity method for ActivityThread:

Public Activity handleLaunchActivity(ActivityClientRecord r, // ······ ······ · final Activity A = performLaunchActivity(r, customIntent); / /......}Copy the code

The handleLaunchActivity method mainly calls performLaunchActivity to start an Activity. Look at the performLaunchActivity method:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {// ······ activity. Attach (appContext, this, getInstrumentation(), R.toy, R.I.dent, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback); / /......}Copy the code

Here, we see a call to the attach method of our Activity. Let’s take a look at what the attach method does.

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) {// ······ mWindow = new PhoneWindow(this, window, activityConfigCallback); // ······ mwindow.setwinDowManager ((WindowManager)context.getSystemService(context.window_service), mToken mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! = 0); / /......}Copy the code

The ATTACH method does two things altogether:

  1. Initialize theWindowThe Window constructor does something else, for example, that we care aboutLayoutInflaterInitialization of.
  2. Initialize theWindowManagerAnd set toWindowGo inside.

3. The Activity’s onCreate

When we first started Android, we knew that calling setContentView in the Activity’s onCreate method would set a layout for the current page. At that time, did you feel it was very magical and full of curiosity about it? Unfortunately, AT that time, I was still unfamiliar with the design of Android and dared not explore it. Today we can officially get inside the setContentView and go to 🤪.

The onCreate procedure for your Activity does more than just setContentView. You can also see from the source code that you can initialize AppCompatDelegate(in this case AppCompatActivity), set a Theme, etc. But that’s not the focus of this article, and I’ll focus on the setContentView method in a later article.

The Activity’s setContentView does nothing; what it actually does is its Window. Let’s look at the Window setContentView method:

    @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;
  }
Copy the code

The setContentView method does the following:

  1. throughinstallDecorMethod to createDecorViewandmContentParent.DecorViewThe creation of thegenerateDecorMethods;mContentParentThe creation of thegenerateLayoutIn the method,generateLayoutThere is an important place in the method set according to the configuration of the propertyWindowtheflagsandfeatures. hereflagsThe effective date of theDecorViewtheupdateColorViewsThe method will be based onflagsTo calculate theDecorViewThe size of the;featuresLoad that layout intoDecorViewThis will affectActivityDefault layout style. But there’s one thing I still don’t know, frommContentParentThe notes of themContentParentIt could beDecorViewBut I didinstallDecorAnd internally calledgenerateLayoutMethods, have not found the relevant possibility.
  2. willContentViewLoaded into themContentParentGo up.

In Android, you can change some attributes by setting flags or addFlags, but have you ever wondered how it works? In fact, the process is very simple, we will not trace the source code, directly look at the call process:

Window# setFlags – > PhoneWindow# dispatchWindowAttributesChanged – > DecorView# updateColorViews.

In the end, the position and size of the DecorView are calculated based on the set Flag. For example, FLAG_FULLSCREEN is set to full screen, and the height of the DecorView is calculated using the updateColorViews method:

        // If we didn't request fullscreen layout, but we still got it because of the // mForceWindowDrawsStatusBarBackground flag, also consume top inset. boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0 && (sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0 && (attrs.flags  & FLAG_LAYOUT_INSET_DECOR) == 0 && mForceWindowDrawsStatusBarBackground && mLastTopInset ! = 0;Copy the code

The above code is very simple to determine whether to consume the height of the status bar. As for other flags, the same is true, those who are interested can have a look.

4. The WindowManager addView

As we know, the ViewParent of DecrorView is ViewRootImpl, and ViewRootImpl triggers the three most important processes of a View. Before we introduce this formally, let’s take a look at a simple structure diagram:

DecorView

  1. Initialize theViewRootImplAnd will beViewRootImplwithWindowBinding.
  2. I’m going to take the one I created earlierDecorViewAdded to theViewRootImplGo inside.

Let’s start by looking at the ActivityThread handleResumeActivity method:

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String Reason) {// ······ ······ final ActivityClientRecord r = performResumeActivity(Token, finalStateRequest, Reason); / /......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; / /......if (a.mVisibleFromClient) {
                if(! a.mWindowAdded) { a.mWindowAdded =true; wm.addView(decor, l); } // ······· ······· ······· ··Copy the code

The handleResumeActivity method does two main things:

  1. callperformResumeActivityThen the callbackActivitytheonResumeMethods.
  2. callWindowManagertheaddViewMethods,DecorViewAdded to theViewRootImpl.

The addView method that is called ends up in WindowManager’s implementation class, WindowManagerImpl, while the addView method of WindowManagerImpl calls The addView method of WindowManagerGlobal. As you can see from the name, Windows ManagerGlobal is global. Let’s look again at the Windows ManagerGlobal addView method:

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {// ······ ViewRootImpl root; View panelParentView = null; Synchronized (mLock) {// ······· ·· 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); } throw e; }}}Copy the code

The addView method does two main things:

  1. Initialize theViewRootImpl, and will theWindowRelevant information is saved, including:ViewRootImpl,DecorViewandLayoutParams.
  2. callViewRootImplthesetViewMethods,DecorViewAdded to theViewRooImplAnd triggerViewThree processes.

We all know that every Window corresponds to a DecorView, and we can see that every DecorView corresponds to a ViewRootImpl, which tells us that if it is a Dialog or other interface of a new Window, There must be a new ViewRootImpl that triggers the View’s three streams, not the ViwRootImpl that hosts the Window.

(1). Why does calling Handler’s POST in the Activity’s onResume method fail to get the View’s width and height?

Before answering this question, it should be noted that this is the Handler post method, not the View post method. Ps: The View post method can get the width and height of the View.

We know that the Activity’s onResume method executes before the ViewRootImpl triggers the measurement process, and the ViewRootImpl triggers the measurement process in the following way:

    void scheduleTraversals() {
        if(! mTraversalScheduled) { mTraversalScheduled =true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
Copy the code

We found that an asynchronous message was posted by the Handler to measure it. However, even though the post is an asynchronous message, the message posted in the onResume method is also executed before it because it follows the post. Therefore, calling Handler’s POST in the Activity’s onResume method does not get the View’s width and height.

5. To summarize

At this point, the Activity structure analysis is similar, and here we make a brief summary.

  1. The structure of the Activity is divided into three layers:Activity,WindowandView.
  2. The structure creation process is divided into three steps: 1. Create an Activity and create a related ActivityWindowManagerandWindowTheta corresponds to thetaActivitytheattachMethod calls; 2. The initializationDecorViewandContentViewTheta corresponds to thetaActivitytheonCreateMethods; 3. Create aViewRootImplAnd will beDecorViewAdded to theViewRootImplAt the same timeViewThe three main processes of the tree.
  3. ingenerateLayoutMethod, depending on the SettingsflagsTo calculate theDecorViewThe size and location of the calculation logic inupdateColorViewsMethod inside; Again, it depends on the SettingsfeaturesMethod to select the default load toDecorViewIn, for example, setNO_ACTION_BARthefeatures, will load without beltActionBarThe layout.