Android source code analysis --View How are added to the screen?

Here Insert Picture Description

Foreword

In Android, we know that we can see the interface is Activity, but we can see how these are being added to the View View of Nick? Today in this article we take a track traceability from source to see exactly how the View to be added to the phone screen. Lengthy article please be patient to read.

View step is added to the Activity

Activity time we write a general resource to resource to load the Activity are written as shown below.

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Here we call the Activity setContentView(@LayoutRes int layoutResID), so let's continue to track the Activity of the source code, look at its setContentView(@LayoutRes int layoutResID)approach.

1、Activity.setContentView(int resourceId)

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

We see here that it calls getWindow()Gets an object called its setContentView(layoutResID)methods. Then we look at getWindow()what is the return object.

  /**
     * Retrieve the current {@link android.view.Window} for the activity.
     * This can be used to directly access parts of the Window API that
     * are not available through Activity/Screen.
     *
     * @return Window The current window, or null if the activity is not
     *         visual.
     */
    public Window getWindow() {
        return mWindow;
    }

We see a direct return here mWindow, where we see a comment that says this method returns the current Activity in the window, if the current Activity is not visible then returns null.

We continue to follow the code, find mWindowcreated, we will find that in the attach()process, find the mWindowassignment.

  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);
       
       /***********省略部分代码*****************/
       
        }

We see here mWindowis a PhoneWindowtarget, so here call PhoneWindowthe setContentView(layoutResID)method.

2、PhoneWindow.setContentView(int layoutResID)

We then look at PhoneWindow.setContentView(int layoutResID)the source code:

    @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;
    }

We see from the first line, the first judge mContentParentis not to null, if it is null, the implementation of the indtallDecor()method, let's continue to look at its source code.

2.1 installDecor()

    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 {
            //给 DecorView 设置 PhoneWindow
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            //初始化 mContentParent
            mContentParent = generateLayout(mDecor);

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            //设置 DecorView 忽略fitsSystemWindows
            mDecor.makeOptionalFitsSystemWindows();

         /*************省略部分代码************************/
         
        }
    }

This part of the code we first saw in the third row to mDecordo the initialization, here mDecorit is DecorView. Then, if mDecornot the current Window is set to null, then to DecorView. Analyzing the back mContentParentis not empty, initialization is empty mContentParent. To sum up this method does the following things:

  1. If mDevor for the air-conditioning generateDecor(-1)initialization DecorView;
  2. DecorView set to empty the current PhoneWindow if mDecor not;
  3. If mContentParent is empty initialization mContentParent;

Below we look at this we first look generateDecor(-1)method is how to initialize DecorViewthe.

I, initialization DevoreView
    protected DecorView generateDecor(int featureId) {
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

Here we see that this method is relatively single piece, its return value is a new direct DecorView object.
Here we look at mDecor.setWindow(this).

II、mDecor.setWindow(this)
    void setWindow(PhoneWindow phoneWindow) {
        mWindow = phoneWindow;
        Context context = getContext();
        if (context instanceof DecorContext) {
            DecorContext decorContext = (DecorContext) context;
            decorContext.setPhoneWindow(mWindow);
        }
    }

This method is very single piece, is the incoming PhoneWindowobject is assigned to DecorViewthe windowproperty.

III, initialization mContentParent
    protected ViewGroup generateLayout(DecorView decor) {
    
    /****************省略部分代码***************************/

        // Inflate the window decor.
        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
        } else 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 ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }
        
        //////////////////////我是华丽的分隔线////////////////////////////////////////
        
        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        
       /****************省略部分代码***************************/
      
        return  contentParent;
    }        

Here we see that there are two parts of the text of the code, we can see that a large section of the dividing line above if/elseis based on different criteria for layoutResourceassignment. This large sections of the code is based on our set of Applicationthemes to select the corresponding resource layout Id loaded.
Then we saw immediately call DecorViewthe onResourcesLoaded(mLayoutInflater, layoutResource)method, so we continue to look at this method specifically what things to do

onResourcesLoaded
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    /**************省略部分代码**************************/
        final View root = inflater.inflate(layoutResource, null);
       /**************省略部分代码**************************/
            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }

Here we look at the code or part of the core, here we see first passed in resource layout inflateand assign it to root, and then rootadded to the DecorViewmedium. It is a word mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);of this code is to load the corresponding resource file to the layoutmDecor .
Here we continue to look at generateLayout(DecorView decor)the method, we found the following it contentParentthrough findViewById(ID_ANDROID_CONTENT)were assigned. Then we look ID_ANDROID_CONTENTID corresponding to the value.

  /**
     * The ID that the main layout in the XML layout file should have.
     */
    public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

Comments say this is the ID of the XML master layout must be present. That is, before the assignment layoutResourcemust be present. Let's grab a top layout xml file to take a look.

R.layout.screen_simple
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

We see R.id.contentdoes exist, and it is a FrameLayout. Here we look at these specific front now to do what?
Shown here holds a PhoneWindow Activity objects, there is a PhoneWindow DecorView, DecorView loaded layout of a basic resource, there are title, ActionBar the like, there is certainly an id below R.id.ccontentresource layout.
Here Insert Picture Description

See here would be finished? No, no, and we continue to look down! ! !

2.2 mLayoutInflater.inflate(layoutResID, mContentParent)

We continue to return to PhoneWiondowthe setContentView(int layoutResID), as follows:

    @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;
    }

We read the earlier installDecor()method, we continue to look down, we see the following call immediately mLayoutInflater.inflate(layoutResID, mContentParent);before an Activity from the incoming resources will be loaded into the layout we initialize earlier mContentParent, that is the resource ID of R.id.ccontentthe Framlayout in. Now let's look at our layout passed in resource file is loaded to where?
[Image dump the chain fails, the source station may have security chain mechanism, it is recommended to save the picture down uploaded directly (img-XcvnGd5G-1585374618136) (http://www.bthvi.work/wp-content/uploads/2020/03 / resource layout file .png)]

to sum up

In this article we start from Ativity of setContentView (layoutResourceId), a step by step tracking Android FrameWork layer source. Explores the code we usually write how the layout resource is loaded into the Activity of. According to our source simply draw a flow chart is as follows:
Activity is added to the flowchart of View

Welcome to leave in the comments area to share your ideas together, grow together. If today's article to help you in work and life, welcome to forward share to more people.

At the same time welcome to join the big front-end learning exchange group I set up, the group we learn from the exchange of Android, Flutter and other knowledge together. From here we can discuss, exchange, upgrade together.

Group number: 872 749 114

My public number

Published 96 original articles · won praise 234 · views 290 000 +

Guess you like

Origin blog.csdn.net/u013132758/article/details/105160186