This is the 7th day of my participation in the August More Text Challenge

The previous learning records are in their own local, by the nuggets this August activity opportunity, but also to challenge themselves. You look at the officer a little praise, younger brother thanks first.

(1) The relationship between Activity,Window and View

View the rendering process from the Activity’s setContent method (2)

View the rendering process from the setContent method of the Activity (3) and then view the Window

See the rendering process from the setContent method of an AppCompatActivity

preface

Before I was familiar with fragments, I could say that I would write an Activity when I saw a page. Before MVVP, I would not throw any code into the Activity, so I was familiar with the Activity. Until one day, a new Android student asked me

Student: How does the XML I write show up in the Activity?

Me: Let’s just pass the setContentView to the Activity. Student: And then?

I: the back is more complex, you just learn Android first don’t delve into details, later slowly tell you again.

The schoolgirl obediently did not ask.

Is there anything wrong with me doing that? No problem. It’s really not recommended for beginners to delve into the details, but to concentrate on learning more about how to use the wheels.

Do I know the answer? I don’t know. Is that a problem? Problem!!

At least also work for a few years, usually oneself also more or less understand the Activity, Window,View of the relationship, the interview also asked, just baidu to a few conclusions said also fuddled the past, but deceiving others is easy, want to deceive yourself is difficult. So I decided to go through what the setContentView is doing.

In the past, I used to read other people’s articles to design some processes. I would not go deep into the code, so that I could not extricate myself and lead to a lot of processes. So this time, I decided to do everything I could to understand every piece of code down to the Native layer.

Where is the XML we wrote for our Activity

Note: there are many pictures in this article.

Lead the environment

AS creates a project. We simply add a TextView to the XML of MainActivity given by default. The code is shown below

The outermost layer is the default constraint layout, plus the IDcl_my_xmlAnd then add its own TextView idtv_hello. I use ThemeTheme.AppCompat.Light.DarkActionBar,MainActivity inheritsActivity.

OK runs the project and begins the journey

Visual observation hierarchy

The effect after running is shown below

Let’s use Android Studio’s view detection tool to take a look.

On the right side, you can see the View of each level. In the middle, you can see the visual View level. On the right side, you can see the specific information of each View.

There are four levels of low-level computation, based on the XML we set for the Activity. From the outside to the inside are respectively DecorView->LinearLayout-> a FrameLayouyt-> our constraint layout. Take a look at each layer individually and see if you can find any clues:

First layer (top layer) : DecorView

At the top is a view called DecorView. No ID information

The second layer: LinearLayout

DecorView has only one view under it. So a LinearLayout also has no ID information.

FrameLayout+View

The third layer has two blocks of layout, above which is a View with the ID of statusBarBackground. Below the status bar is a FrameLayout with an ID called Content.

Layer 4: XML for setContent

The fourth layer is the my_xml layout we wrote ourselves, which is added to a FrameLayout with an ID of Content on the top layer.

There is another layer of TextView layout that we wrote ourselves, so there is no need to continue the analysis, because there are countless more layers that can be nested during actual development.

Conclusion the hierarchy

After the visual analysis of AS, we have a very intuitive conclusion:

We set the XML layout for the Activity and finally add it to a FrameLayout with the ID content. The FrameLayout sibling is a View with the id statusBarBackground, the familiar status bar. They’re all in a vertical LinearLayout, which in turn is in the top-level DecorView.

doubt

A DecorView is the top-level view we can see, but we are setting the content for the Activity. What is the relationship between this DecorView and the Activity?

AS layout visualization tool is still very easy to use, can be very intuitive to our conclusions. But we’re programmers. Girlfriends lie, but code doesn’t. We just trust code. So, with that in mind, we went to the code and found evidence.

Code demonstrates

Our goal is to figure out how setContent adds our written XML to the Activity, so we’ll just keep an eye on where our XML goes and eventually figure it out. Let’s start with the setContentView method

android.app.Activity

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

The second line reads that the method name should be the initialization status bar strong correlation, we don’t care, keep an eye on our layoutResID, so look at the first line of code.

The getWindow method returns a variable of type Window, mWindow, which is assigned to the Attach method of the Activity mentioned earlier in the Activity launch process

What is a mWindow

public Window getWindow() {
        return mWindow;
    }
    
Copy the code
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, IBinder assistToken) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); MWindow = new PhoneWindow(this, Window, activityConfigCallback); mWindow.setWindowControllerCallback(mWindowControllerCallback); Pass the Activity to Window mwindow.setcallback (this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); . Pass system WindowManager to PhoneWindow mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! = 0); . }Copy the code

You can see that mWindow is actually of type PhoneWindow because Window is an abstract class and its only implementation is PhoneWindow.

After creating an instance of mWindow, the current Activity is set as a callback. It is mainly used for callback processing of some events.

SetWindowManager basically passes system WindowManager to PhoneWindow, and finally holds a WindowManagerImpl reference in PhoneWindow. Future articles will provide a more detailed explanation of Windows, and the first article in this series will start with the general framework.

You may often see that the only implementation of Window is PhoneWindow, but you don’t understand it. In fact, the definition of Window is clearly written, as shown in the following figure

The setContent PhoneWindow

So our layout is now in PhoneWindow

com.android.internal.policy.PhoneWindow ...... 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) {Key code 1 (initialize DecorView and mContentParent) 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 {key code 2 (throw our layout to the mContentParent) mLayOutInflater.inflate (layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb ! = null && ! isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }Copy the code

Let’s look at the first key code. We don’t know what the mContentParent is, except that it is a ViewGroup and has a frame layout with the id content. If mContentParent ==null is set up, installDecor will be set up, so installDecor must be set up, and one thing can be confirmed. Initialize this thing called mContentParent.

installDecor

com.android.internal.policy.PhoneWindow ...... // This is the top-level view of the window, containing the window decor. This is the top-level view of the window, including the private DecorView mDecor; . private void installDecor() { mForceDecorInstall = false; If (mDecor == null) {DecorView mDecor = generateDecor(-1); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (! mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures ! = 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); }} else {bind DecorView to PhoneWindow mDecor. SetWindow (this); } if (mContentParent == null) {create mContentParent mContentParent = generateLayout(mDecor); . }}Copy the code

The installDecor method starts out judging mDecor. MDecor is a DecorView from the class variable, and the comment says this is the top-level view of the window. Visualize what you see at the beginning of the combination. So we prove the first conclusion:

The mDecor variable of type DecorView is our top-level view. This DecorView is bound to PoneWindow, and PhoneWindow is bound to the Activity in the attch method that A executes when creating the Activity.

When we check the view, we also explain why we can’t see the Activity

The Activity does not exist as a view that we can see. It controls certain operations and callbacks. It has a PhoneWindow, and the top-level view that we can see is attached to the PhoneWindow

GenerateDecor (Create top-level view DecorView)

See how this top-level view, DecorView, is created

protected DecorView generateDecor(int featureId) { // System process doesn't have application context and in that case we need to directly use // the context we have. Otherwise we want the application context, so we don't cling to the // activity. Context context; if (mUseDecorContext) { Context applicationContext = getContext().getApplicationContext(); if (applicationContext == null) { context = getContext(); } else { context = new DecorContext(applicationContext, this); if (mTheme ! = -1) { context.setTheme(mTheme); } } } else { context = getContext(); } return new DecorView(context, featureId, this, getAttributes());Copy the code

Okay, this is just a DecorView that inherits FrameLayout, and doesn’t see the view from the previous visualization analysis, which should have a linear layout. Let’s leave it there and see if we can find anything.

After you create the DecorView, call mDecor. SetWindow (this) to bind it to the current PhoneWindow

com.android.internal.policy.DecorView public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { ...... Void setWindow(PhoneWindow PhoneWindow) {// Now DecorView is bound to PhoneWindow mWindow = PhoneWindow; Context context = getContext(); if (context instanceof DecorContext) { DecorContext decorContext = (DecorContext) context; decorContext.setPhoneWindow(mWindow); } if (mPendingWindowBackground ! = null) { Drawable background = mPendingWindowBackground; mPendingWindowBackground = null; setWindowBackground(background); }}... }Copy the code

Moving on, don’t forget that we entered this method to see what the mContentParent thing is.

Friendly reminder: high energy ahead, a lot of code is about to arrive at the battlefield

mContentParent = generateLayout(mDecor);

GenerateLayout (create the upper content of our XML)
protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. TypedArray a = getWindowStyle();  . if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) { requestFeature(FEATURE_ACTION_BAR_OVERLAY); } a lot of code like this is omitted here, which is the same as setting the Window to different styles, such as the transparent status bar, which are handled here...... // The XML file used by the Inflate the window decor. DecorView int layoutResource; if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) ! = 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_title_icons; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); // System.out.println("Title Icons!" ); } else if{...... } omits a lot of code to match which XML file the final DecorView needs to use based on the system version and theme style. . mDecor.startChanging(); Set matched XML styles to mDecor (top-level DecorView) mDecor. OnResourcesLoaded (mLayoutInflater, layoutResource); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); // There must be a ViewGroup with id content, Throw new RuntimeException("Window couldn't find content container view"); if (contentParent == null) {throw new RuntimeException("Window couldn't find content container view"); } mDecor.finishChanging(); Return contentParent; }Copy the code

If you also click generateLayout and watch together. I’m going to get scared when I go in. It’s too long. Here’s a way I can look at this code:

I know I went into this method to find out how mContent was created, so I went straight to see what it returned. And see if we can see what we picked up on the way back to the target.

Take a look at what I see this method doing:

  1. Read WindowStyle to set different styles for Windows
  • This is mainly dealing with Window styles, such as the transparent status bar
  1. Match which XML file is required for the final DecorView based on the system version and theme style
  • The XML matched to the top-level view DecorView is actually the second-level view we see in view detection
  • In this step, different conditions set different layouts for the top-level view DecorView. I clicked on several different XML files and found that they were all different, but they all had the same layout: the layout with the ID content and type FrameLayout. With the previous view tool we can be sure that this is the third layer layout we are looking for.
  • We can also determine that the container with the id content must exist by throwing an exception with contentParent == null
  • The second layer is not necessarily a vertical linear layout either, depending on the specific implementation of the XML that is finally set to the view DecorView.
  1. Find the contentParent of type FrameLayout in the current PhoneWindow. Return the assignment to mContentParent
  • Because the DecorView is already bound to the current PhoneWindow, findViewById is actually looking for the View from the DecorView
  • Id constants ID_ANDROID_CONTENT click here to see can be defined as com. Android. Internal. R.i, dc ontent so mContentParent is the third layer id to the content of the frame layout.

Go back to the setContentView

Back in the PhoneWindow setContentView method, if you forget the code, you can go back.

Following the previous logic, the first key installDecor creation initializes the DecorView and mContentParent. The second mlayoutinflater.inflate (layoutResID, mContentParent); It would be too easy to set the XML we wrote into the mContentParent container, which is the FrameLayout with the ID content.

Combing level

So far, we have found four levels of layout relationships in the code, and I think the reader should have a clear idea of the relationship between Activity, Window, and View. I’ll post another picture, too

conclusion

  1. At first, we wrote an app with only one TextView. After running it to the device, we checked the view hierarchy through the view detection tool of AS. After all, seeing is believing.
  2. We then start to demonstrate our guess with the Activity’s setContent method, knowing that mWindow was actually created and that PhoneWindow is the real type of mWindow. It’s also the only line of sight class for Windows in Android
  3. We find the true view of the setContent method in PhoneWindow, know how the DecorView comes from, how it relates to PhoneWindow, and where the layout with id content is located. It also found that the XML file we wrote was finally put into the layout with the ID content. So you can see why it’s called setContentView instead of setView.

How about knowing what’s gonna happen

In fact, although we know that the Activity PhoneWindow is in the Attach method, we haven’t said how they actually create the relationship. At the same time, we only analyze that the written XML is eventually set to the DecorView, but the Activity is not aware of that. This includes how views are drawn to the screen. How the corresponding click event is fed back to the Activity… Listen to the breakdown next time. When asked about the relationship between Activity,Window, and View in future interviews, I hope you will not just say “Window is a Window,View is a sticker, and Activity is the controller”. Although wrong every time. But it’s a little too… That what.