preface

We all know that a DecorView is the top-level View (root View). How is it created and used? Through this article, you will learn:

3. Create a Window/Activity View from a Window View

DecorView creation process

To review the Activity creation process:

AMS manages the Activity lifecycle by telling the ActivityThread each time the Activity state is switched by Binder. The ActivityThread switches to the main thread (UI thread) by Handler. Finally, the familiar onCreate(xx)/onResume() methods are called. This time we’ll focus on the setContentView(xx) method.

Simple layout analysis

I believe we all know that the method is to add our layout file to a ViewGroup id ** “android.r.i. C tent” **, we only need to care about the content of the layout. Is R.I.D.C tent the root layout of the entire View tree? Take a look at the simplest Activity layout:

my_layout.xml <? The XML version = "1.0" encoding = "utf-8"? > <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"  android:background="@color/colorGreen" android:id="@+id/my_root" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/btn" android:text="hello" android:onClick="onClick" android:layout_width="wrap_content" android:layout_height="wrap_content"> </Button> </FrameLayout>Copy the code

Effect:


SetContentView (xx) source code parsing

Start with Activity onCreate(xx)

AppCompatDelegateImpl

Override protected void onCreate(Bundle savedInstanceState) {Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.layout_path); }Copy the code

Create and initialize AppCompatDelegateImpl

AppCompatActivity.java protected void onCreate(@Nullable Bundle savedInstanceState) { final AppCompatDelegate delegate =  getDelegate(); delegate.installViewFactory(); // Give the AppCompatDelegate agent delegate.oncreate (savedInstanceState); super.onCreate(savedInstanceState); }Copy the code

The AppCompatActivity gives work to AppCompatDelegate, which is an abstract class that really works on its subclass: AppCompatDelegateImpl.

@Override public void onCreate(Bundle savedInstanceState) { ensureWindow(); If (mWindow == null && mHost instanceof Activity) {private void ensureWindow() {// omits //mHost instanceof Activity; attachToWindow(((Activity) mHost).getWindow()); } } private void attachToWindow(@NonNull Window window) { if (mWindow ! = null) { throw new IllegalStateException( "AppCompat has already installed itself into the Window"); $compatWindowCallback = new appWindowCallback (callback); $compatWindowCallback = new appWindowCallback (callback); window.setCallback(mAppCompatWindowCallback); // omit // use activity window mWindow = window; }Copy the code

The main job of the above code is to associate AppCompatDelegateImpl with the Activity’s window variable.

Then look at setContentView(r.layout.layout_path) this Activity’s setContentView(xx) finally calls the AppCompatDelegateImpl setContentView(xx) :

AppCompatDelegateImpl. Java @ Override public void the setContentView (int resId) {/ / create SubDecor, From the name, we can guess that it is the ensureSubDecor() of DecorView's child. ViewGroup contentParent = mSubDecor. FindViewById (Android.r.d.c. tent); . / / the child View in the empty content contentParent removeAllViews (); // Load the layout set in the activity and add it to the content layoutinflater.from (mContext).inflate(resId, contentParent); }Copy the code

As you can see, our custom layout is finally added to contentParent, which is found from mSubDecor, so we’ll focus on the ensureSubDecor() method. The createSubDecor() method is called in ensureSubDecor to createSubDecor.

SubDecor create

AppCompatDelegateImpl. Java private ViewGroup createSubDecor () {/ / according to the theme of the Activity set different Settings window feature TypedArray = a mContext.obtainStyledAttributes(R.styleable.AppCompatTheme); // Make sure the window is already associated with ensureWindow(); // Get the DecorView, if not create mwindow.getdecorView (); ViewGroup subDecor = null; // have the title if (! MWindowNoTitle) {// Load different layout files for subDecor based on conditions if (mIsFloating) {subDecor = (ViewGroup) inflater.inflate( R.layout.abc_dialog_title_material, null); } else if (mHasActionBar) {// Load subDecor layout subDecor = (ViewGroup) LayoutInflater. From (themedContext) .inflate(R.layout.abc_screen_toolbar, null); MDecorContentParent = (DecorContentParent) subDecor. FindViewById (R.i.D.decor_content_parent); mDecorContentParent.setWindowCallback(getWindowCallback()); }} else {// omitted} // Title bar title if (mDecorContentParent == null) {mTitleView = (TextView) subDecor. FindViewById (R.i.TATE); } // Find subDecor sub layout, Call it contentView Final ContentFrameLayout contentView = (ContentFrameLayout) subDecor. FindViewById ( R.id.action_bar_activity_content); // Find the content layout in window, Final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(Android.r.i. D.c ontent);  if (windowContentView ! // Remove the children of windowContentView one by one, And will be added to the contentView while (windowContentView. GetChildCount () > 0) {final View child = windowContentView. GetChildAt (0); windowContentView.removeViewAt(0); contentView.addView(child); } // Remove the windowContentView ID, which was previously called Content WindowContentView.setid (view.no_id); // assign the "content" name to contentView contentview.setid (android.r.i content); } // Add subDecor to the Window contentView, which is actually a child of the DecorView. After this method, mwindow.setContentView (subDecor) is analyzed. return subDecor; }Copy the code

This method is long and omits some details. Its main function is:

  • Load different layout files as subDecor according to different preconditions
  • Add subDecor to your DecorView. SubDecor is itself a ViewGroup

The subDecor was created, but when was the DecorView created? CreateSubDecor () has this code:

mWindow.getDecorView()
Copy the code

DecorView create

MWindow is the Activity’s window variable, and its implementation class is PhoneWindow.

PhoneWindow.java View getDecorView() { if (mDecor == null || mForceDecorInstall) { installDecor(); } return mDecor; } private void installDecor() {//mDecor as PhoneWindow variable if (mDecor == null) {// New DecorView, Window mDecor = generateDecor(-1); } if (mContentParent == null) {// Create DecorView child layout mContentParent = generateLayout(mDecor); } } protected DecorView generateDecor(int featureId) { Context context; if (mUseDecorContext) { Context applicationContext = getContext().getApplicationContext(); if (applicationContext == null) { context = getContext(); } else {//applicationContext is Application //getContext is Activity //DecorContext inherits from ContextThemeWrapper context = new DecorContext(applicationContext, getContext()); if (mTheme ! = -1) { context.setTheme(mTheme); } } } else { context = getContext(); Return new DecorView(context, featureId, this, getAttributes()); }Copy the code

From the above we can learn that:

DecorView inherits from FrameLayout

Note: DecorContext ¶

Let’s see.

mContentParent = generateLayout(mDecor);

Phonewindow.java protected ViewGroup generateLayout(DecorView decor) {// getWindowStyle TypedArray a = getWindowStyle(); < span style = "box-sizing: border-box! Important; word-wrap: break-word! Important; WindowManager.LayoutParams params = getAttributes(); Int layoutResource; int features = getLocalFeatures(); If ((features & (1 << FEATURE_SWIPE_TO_DISMISS))! = 0) { layoutResource = R.layout.screen_swipe_dismiss; setCloseOnSwipeEnabled(true); } else if (features) {// omit} else {// default to load the layout layoutResource = r.layout.screen_simple; } // Instantiate the layout identified above and add it to the DecorView mDecor. OnResourcesLoaded (mLayoutInflater, layoutResource); / / get called ID_ANDROID_CONTENT child View / / public static final ints ID_ANDROID_CONTENT = com. Android. Internal. R.i, dc ontent; ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); return contentParent; }Copy the code

Load the determined layout and add it to the DecorView.

void onResourcesLoaded(LayoutInflater inflater, Int layoutResource) {// Instantiate the layout final View root = inflater.inflate(layoutResource, null); if (mDecorCaptionView ! = null) {// omit} else {// add to DecorView addView(root, 0, new viewGroup.layoutParams (MATCH_PARENT, MATCH_PARENT)); } // record root mContentRoot = (ViewGroup) root; }Copy the code

In summary, what does mWindow.getdecorView () do?

  • Create a DecorView and associate the PhoneWindow with a DecorView(hold each other)
  • Determine the DecorView child layout based on the feature and fill the DecorView with fillers

DecorView, SubDecor, decor, decor

1. Create a DecorView and add its child View, called mContentRoot in the DecorView. The sub-view resource id used in the current scenario: r.layout.screen_simple; 2, a child View named “R.D.C. tent” in mContentRoot, which is called in PhoneWindow: MContentParent 3, subDecor resource ID :R.layout.abc_screen_toolbar, Instantiate the resource file to get subDecor instance. 4. SubDecor has a subview resource ID: R.D.action_bar_activity_content, which is named on AppCompatDelegateImpl as follows: Add subDecor. Take out the mContentParent in the DecorView, remove the child View, and add the removed child View to the contentView in the subDecor. 7. Add subDecor to the DecorView with mwindow.setContentView (subDecor)

Mwindow.setcontentview (subDecor)

PhoneWindow.java public void setContentView(View view, Viewgroup.layoutparams params) {if (mContentParent == null) {// Null, DecorView does not construct installDecor(); } else if (! Loophole hasFeature (FEATURE_CONTENT_TRANSITIONS)) {/ / clear View mContentParent. RemoveAllViews (); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS) {// omit} else {//View added to mContentParent McOntentparent.addview (View, params); } // omit}Copy the code

That’s how you create a DecorView. After the DecorView is created, add the custom layout to the DecorView layout named R.I.D.C. Tent. So now the setContentView is done, and the old convention is to graph the whole process.

The setContentView graphic

DecorView relationship with Window/Activity

Activity: We usually treat the Activity as a page, visually this page carries our layout display and the corresponding logic processing. Activities have a life cycle and do different things in different states.

Window, as the name implies, is the Window we see, and the content displayed by an Activity is actually displayed on the Window, which corresponds to a PhoneWindow.

The DecorView Window itself is abstract and can be thought of as a management thing. It manages the ViewTree, and the DecorView is the root of the ViewTree. Our concrete display of the interface is done through the View. By adding the designed View to the DecorView, the entire ViewTree is constructed and finally added to the Window. Therefore:

  • The Activity manages Windows, the Window manages DecorView, and the Activity indirectly manages the DecorView
  • The Activity creates the corresponding Window when it is in the “Create” state, creates a DecorView in the setContentView, and finally adds a custom layout to the DecorView. The Activity has been created, the Window has been created, the DecorView has been created, and the ViewTree has been created.
  • The Acitivity in the “Resume” state adds a DecorView to the Window via the Window management class and submits a request to update the DecorView. When the next screen refresh signal comes, execute the ViewTree three processes (measure, layout, draw), and the final effect is that our custom layout appears on the screen.

Note: indicates that the state execution time is in the ActivityThread.

It still doesn’t look right. The connection we’re talking about is actually “you either hold me or I reference you” in the code. Let’s look at the references among the three in the class diagram:

As you can see, the Window acts as the linker between the Activity and the DecorView. Note: The Activity also holds a reference to the DecorView, but it does not provide an interface to get it. So you typically get the Window through your Activity and then get the DecorView. From the UI perspective, it can be understood as follows:

Note: This diagram is just for understanding the abstract relationship; there is no actual dimension for the Activity.

Note: The above choices for DecorView, subDecor, title bar, layout file, and block size are based on the current demo. The theme you use and the properties you set may cause the layout to look different from this article. Source code based on: Android 10.0