The process of the setContentView

Based on the sdk30

  • SetContentView is an important method of presenting the page, and it is required to display the XML on the page
  • SetContentView has two ways of passing arguments either through layoutID or directly through the view and it’s essentially the same thing
  • It is worth noting that the process of inheriting AppCompatActivity and Activity is slightly different.

The process of the Activity

  • Too much source code only copied the key code
  • getWindow().setContentView(layoutResID);
  • GetWindow () refers to the Window, the Window is an abstract class, the only implementation class is PhoneWindow, MockWindow not in com. He is android. Setupwizardlib. Test. Util directory
  • Activtiy window is instantiated in the attach method, the attach is the ActivityThread performLaunchActivity call, (big mouth a Activtiy, Application of instantiation is also in this implementation, Activtiy and Application are both created by reflecting newInstance.)

  • The PhoneWindow setContentView method is called
@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
  • Start analyzing the installDecor method and see how generateDecor implements DecorView.
private void installDecor() { //... mForceDecorInstall = false; if (mDecor == null) { mDecor = generateDecor(-1); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (! mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures ! = 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } else { mDecor.setWindow(this); } / /... }Copy the code
  • GenerateDecor method, directly to the context and WindowManager. LayoutParams instantiation DecorView (note if it is mUseDecorContext is true, PhoneWindow(Context Context, Window preservedWindow, ActivityConfigCallback ActivityConfigCallback) MUseDecorContext is tur. This construct is called only by the PhoneWindow that comes out of the Activity new, and the DecorView context is getApplicationContext().

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
  • MContentParent = generateLayout(mDecor); This part is actually kind of fucked up and you only have to focus on 2 points
protected ViewGroup generateLayout(DecorView decor) { // ... Omit mDecor. OnResourcesLoaded (mLayoutInflater layoutResource); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); / /... Omit}Copy the code
  • The above is to setFlags by determining the different attributes and get the system default XML file that determines the layoutResource

  • OnResourcesLoaded Adds layoutResource to the DecorView

  • FindViewById (ID_ANDROID_CONTENT) this is the findViewById that goes through the DecorView and then the findViewTraversal that goes through the Viewgroup to get the view. (Activtiy and Diaolog findViewById are both found in DecorView findViewById)
  • Look at the last part of the setContentView
If (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); }Copy the code
  • HasFeature (FEATURE_CONTENT_TRANSITIONS) has no transitions, and if so, call transitionTo
 private void transitionTo(Scene scene) {
        if (mContentScene == null) {
            scene.enter();
        } else {
            mTransitionManager.transitionTo(scene);
        }
        mContentScene = scene;
    }
Copy the code
  • The first time the mContentScene must be null look at scene.Enter ();
public void enter() { // Apply layout change, if any if (mLayoutId > 0 || mLayout ! = null) { // empty out parent container before adding to it getSceneRoot().removeAllViews(); if (mLayoutId > 0) { LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot); } else { mSceneRoot.addView(mLayout); } } // Notify next scene that it is entering. Subclasses may override to configure scene. if (mEnterAction ! = null) { mEnterAction.run(); } setCurrentScene(mSceneRoot, this); }Copy the code
  • Seeing this draws a conclusion that there are no animations that will go LayoutInflater. Inflater methods
  • Finally, take a look at what the Inflater is doing: Find the inflate(XmlPullParser Parser Parser, @Nullable ViewGroup root, Boolean attachToRoot) by calling the inflate
  • It’s good to know that XmlResourceParser is an XML parser, so if you’re interested, Google it.
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); / /... Omit the if (TAG_MERGE equals (name)) {if (root = = null | |! attachToRoot) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, inflaterContext, attrs, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, inflaterContext, attrs); / /... Omit rInflateChildren(Parser, temp, attrs, true); if (DEBUG) { System.out.println("-----> done inflating children"); } // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root ! = null && attachToRoot) {// addView to rootview root.addview (temp, params); } } return result; }}Copy the code
  • If (tag_merge.equals (name)) means that if the root layout is a merge then the rInflate method is used.
void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { final int depth = parser.getDepth(); int type; boolean pendingRequestFocus = false; while (((type = parser.next()) ! = XmlPullParser.END_TAG || parser.getDepth() > depth) && type ! = XmlPullParser.END_DOCUMENT) { if (type ! = XmlPullParser.START_TAG) { continue; } final String name = parser.getName(); if (TAG_REQUEST_FOCUS.equals(name)) { pendingRequestFocus = true; consumeChildElements(parser); } else if (TAG_TAG.equals(name)) { parseViewTag(parser, parent, attrs); } else if (TAG_INCLUDE.equals(name)) { if (parser.getDepth() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, context, parent, attrs); } else if (TAG_MERGE.equals(name)) { throw new InflateException("<merge /> must be the root element"); } else { final View view = createViewFromTag(parent, name, context, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflateChildren(parser, view, attrs, true); viewGroup.addView(view, params); } } if (pendingRequestFocus) { parent.restoreDefaultFocus(); } if (finishInflate) { parent.onFinishInflate(); }}Copy the code
  • While traversal to determine the name of the XML to parse the different tags to focus on else createViewFromTag
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { //... Omit try {View View = tryCreateView(parent, name, context, attrs); if (view == null) { final Object lastContext = mConstructorArgs[0]; mConstructorArgs[0] = context; try { if (-1 == name.indexOf('.')) { view = onCreateView(context, parent, name, attrs); } else { view = createView(context, name, null, attrs); } } finally { mConstructorArgs[0] = lastContext; } } return view; } / /... Omit}Copy the code
  • So let’s look at tryCreateView
public final View tryCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) { if (name.equals(TAG_1995)) { // Let's party like it's 1995! return new BlinkLayout(context, attrs); } View view; if (mFactory2 ! = null) { view = mFactory2.onCreateView(parent, name, context, attrs); } else if (mFactory ! = null) { view = mFactory.onCreateView(name, context, attrs); } else { view = null; } if (view == null && mPrivateFactory ! = null) { view = mPrivateFactory.onCreateView(parent, name, context, attrs); } return view; }Copy the code
  • CreateViewFromTag view = tryCreateView(parent, name, Context, attrs); MFactory and mFactory2 are assigned to the constructor method when the LayoutInflater’s onCreateView is empty
protected LayoutInflater(LayoutInflater original, Context newContext) {
        mContext = newContext;
        mFactory = original.mFactory;
        mFactory2 = original.mFactory2;
        mPrivateFactory = original.mPrivateFactory;
        setFilter(original.mFilter);
        initPrecompiledViews();
    }
Copy the code
  • To explore how layoutInflaters are created let’s go back to PhoneWindow
public PhoneWindow(Context context) { super(context); mLayoutInflater = LayoutInflater.from(context); mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(), DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) ! = 0; }Copy the code
  • Layoutinflaters are created when attach instantiates a Phoneview and look directly at layoutinflaters
   public static LayoutInflater from(Context context) {
       LayoutInflater LayoutInflater =
               (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
       if (LayoutInflater == null) {
           throw new AssertionError("LayoutInflater not found.");
       }
       return LayoutInflater;
   }

Copy the code
  • Context.getsystemservice (), now back to the Activity, After all, the context is from the Activity’s ContextThemeWrapper (ContextThemeWrapper inherits ContextWrapper, ContextWrapper inherits the context abstract class).
  • GetSystemService goes to ContextThemeWrapper’s getSystemService via super
 @Override
   public Object getSystemService(String name) {
       if (LAYOUT_INFLATER_SERVICE.equals(name)) {
           if (mInflater == null) {
               mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
           }
           return mInflater;
       }
       return getBaseContext().getSystemService(name);
   }
Copy the code
  • If it’s LAYOUT_INFLATER_SERVICE and mInflater is null, go directly to cloneInContext. Go ahead and see what getBaseContext() is
  • Return to the Attach method of the Activtiy and see that a Context was passed and then attachBaseContext(Context) was called to assign getBaseContext(). Murphy…
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); / / to omit... }Copy the code
  • ActivityThread. PerformLaunchActivity call activity. The attach
Private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {// Omitted... ContextImpl appContext = createBaseContextForActivity(r); / / to omit... 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, r.assistToken); / /...Copy the code
  • So it’s going to be ContextImpl’s getSystemService
@Override public Object getSystemService(String name) { //... Omit the return SystemServiceRegistry. GetSystemService (this, name); }Copy the code
  • The getSystemService SystemServiceRegistry
public static Object getSystemService(ContextImpl ctx, String name) { if (name == null) { return null; } final ServiceFetcher<? > fetcher = SYSTEM_SERVICE_FETCHERS.get(name); / /... Omit final Object ret = fetcher.getService(CTX); / /... Omit the return ret; }Copy the code
  • SYSTEM_SERVICE_FETCHERS this one puts a lot of objects when the SystemServiceRegistry class is loaded, and then gets objects by name such as LAYOUT_INFLATER_SERVICE
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, new CachedServiceFetcher<LayoutInflater>() { @Override public LayoutInflater createService(ContextImpl ctx) { return new  PhoneLayoutInflater(ctx.getOuterContext()); }});Copy the code
  • So the LayoutInflater. The from (getBaseContext ()). CloneInContext (this) is actually called the PhoneLayoutInflater. CloneInContext
   public LayoutInflater cloneInContext(Context newContext) {
        return new PhoneLayoutInflater(this, newContext);
    }
    
     protected PhoneLayoutInflater(LayoutInflater original, Context newContext) {
        super(original, newContext);
    }
Copy the code
  • View View = tryCreateView(parent, name, Context, attrs); Null so I’m going to go onCreateView or createView
 View view = tryCreateView(parent, name, context, attrs);

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
Copy the code
  • Check if -1== name.indexof (‘.’) if name is not included. Such as TextView ImgaeView onCreateView, otherwise such as custom com. XXX. XxxView, androidx. Constraintlayout. Widget. Constraintlayout, Androidx. Recyclerview. Widget. Recyclerview these needs in the XML using the full path of the view createView
  • MInflater = LayoutInflater. From (getBaseContext()).cloneInContext(this) So going to see next PhoneLayoutInflater. OnCreateView nor rewrite.
private static final String[] sClassPrefixList = { "android.widget.", "android.webkit.", "android.app." }; protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { for (String prefix : sClassPrefixList) { try { View view = createView(name, prefix, attrs); if (view ! = null) { return view; } } catch (ClassNotFoundException e) { // In this case we want to let the base class take a crack // at it. } } return super.onCreateView(name, attrs); }Copy the code
  • Let mDecor. OnResourcesLoaded (mLayoutInflater, layoutResource) in generateLayout. , mLayoutInflater layout for R.l ayout screen_simple, his first layout is a LinearLayout and LinearLayout. The full path to the android widget. The LinearLayout. Then look the createView PhoneLayoutInflater without rewriting the createView, so back to the LayoutInflater. CreateView
public final View createView(@NonNull Context viewContext, @NonNull String name, @Nullable String prefix, @Nullable AttributeSet attrs) throws ClassNotFoundException, InflateException { Constructor<? extends View> constructor = sConstructorMap.get(name); Class<? extends View> clazz = null; / /... try { //... if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it clazz = Class.forName(prefix ! = null ? (prefix + name) : name, false, mContext.getClassLoader()).asSubclass(View.class); / /... constructor = clazz.getConstructor(mConstructorSignature); / /... Omit 】 / /... final View view = constructor.newInstance(args); / /.. return view; } / /... Omit}Copy the code
  • See Class as a view completed by prefix and name, instantiated by constructive.newinstance. Such as LinearLayout cycle through completion android. Widget. LinearLayout to use reflection to create this that is why the TextView, ImageView, LinearLayout these don’t have to write the full path in XML (these are all in “Android.widget.”,” Android.webKit.”, In the “android.app.” directory, the path is automatically completed by the for loop sClassPrefixList), and other needs.
  • If (-1 == name.indexof (‘.’)) else check is not needed.
  • Take a look at the rInflateChildren create child View
 final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
    }
Copy the code
  • The rInflate is done
  • Go back to the judgment if (tag_merge.equals (name)) in the inflate and look at the else part
if (TAG_MERGE.equals(name)) { if (root == null || ! attachToRoot) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, inflaterContext, attrs, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, inflaterContext, attrs); / /... rInflateChildren(parser, temp, attrs, true); if (DEBUG) { System.out.println("-----> done inflating children"); } // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root ! = null && attachToRoot) { root.addView(temp, params); }}Copy the code
  • Again createViewFromTag and rInflateChildren, the process is already over. Inflate is simply the name that is parsed through XML

AttributeSet creates the view and then addView to rootView.

  • Let’s go back to the setContentView code in PhoneWindow
 mLayoutInflater.inflate(layoutResID, mContentParent);
Copy the code
  • Add your Activty XML layout to the parent layout

Take a look at the setContentView process for AppCompatActivity

@Override public void setContentView(@LayoutRes int layoutResID) { getDelegate().setContentView(layoutResID); } public AppCompatDelegate getDelegate() { if (mDelegate == null) { mDelegate = AppCompatDelegate.create(this, this); } return mDelegate; } private static AppCompatDelegate create(Context context, Window window, AppCompatCallback callback) { if (Build.VERSION.SDK_INT >= 24) { return new AppCompatDelegateImplN(context, window, callback); } else if (Build.VERSION.SDK_INT >= 23) { return new AppCompatDelegateImplV23(context, window, callback); } else { return new AppCompatDelegateImplV14(context, window, callback); }} AppCompatDelegateImplN inherits AppCompatDelegateImplV23 AppCompatDelegateImplV23 inherits AppCompatDelegateImplV14 AppCompatDelegateImplV14 inheritance AppCompatDelegateImplV9 AppCompatDelegateImplV9 inheritance AppCompatDelegateImplBase implementation MenuBuilder. Callback, LayoutInflater. Factory2 interfaceCopy the code
  • Jump to the setContentView of AppCompatDelegateImplV9 by doing the source code
   @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }
    
    private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            //重点
            mSubDecor = createSubDecor();

            // If a title was set before we installed the decor, propagate it now
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                onTitleChanged(title);
            }

            applyFixedSizeWindow();

            onSubDecorInstalled(mSubDecor);

            mSubDecorInstalled = true;

            // Invalidate if the panel menu hasn't been created before this.
            // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
            // being called in the middle of onCreate or similar.
            // A pending invalidation will typically be resolved before the posted message
            // would run normally in order to satisfy instance state restoration.
            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (!isDestroyed() && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }
    
    private ViewGroup createSubDecor() {
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);

        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
            a.recycle();
            throw new IllegalStateException(
                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
        }

        if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
            requestWindowFeature(Window.FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
        }
        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
        }
        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
            requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
        }
        mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
        a.recycle();

        //重点
        // Now let's make sure that the Window has installed its decor by retrieving it
        mWindow.getDecorView();

        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;


        if (!mWindowNoTitle) {
            if (mIsFloating) {
                // If we're floating, inflate the dialog title decor
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_dialog_title_material, null);

                // Floating windows can never have an action bar, reset the flags
                mHasActionBar = mOverlayActionBar = false;
            } else if (mHasActionBar) {
                /**
                 * This needs some explanation. As we can not use the android:theme attribute
                 * pre-L, we emulate it by manually creating a LayoutInflater using a
                 * ContextThemeWrapper pointing to actionBarTheme.
                 */
                TypedValue outValue = new TypedValue();
                mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);

                Context themedContext;
                if (outValue.resourceId != 0) {
                    themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
                } else {
                    themedContext = mContext;
                }

                // Now inflate the view using the themed context and set it as the content view
                subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                        .inflate(R.layout.abc_screen_toolbar, null);

                mDecorContentParent = (DecorContentParent) subDecor
                        .findViewById(R.id.decor_content_parent);
                mDecorContentParent.setWindowCallback(getWindowCallback());

                /**
                 * Propagate features to DecorContentParent
                 */
                if (mOverlayActionBar) {
                    mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
                }
                if (mFeatureProgress) {
                    mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
                }
                if (mFeatureIndeterminateProgress) {
                    mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
                }
            }
        } else {
            if (mOverlayActionMode) {
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_screen_simple_overlay_action_mode, null);
            } else {
                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
            }

            if (Build.VERSION.SDK_INT >= 21) {
                // If we're running on L or above, we can rely on ViewCompat's
                // setOnApplyWindowInsetsListener
                ViewCompat.setOnApplyWindowInsetsListener(subDecor,
                        new OnApplyWindowInsetsListener() {
                            @Override
                            public WindowInsetsCompat onApplyWindowInsets(View v,
                                    WindowInsetsCompat insets) {
                                final int top = insets.getSystemWindowInsetTop();
                                final int newTop = updateStatusGuard(top);

                                if (top != newTop) {
                                    insets = insets.replaceSystemWindowInsets(
                                            insets.getSystemWindowInsetLeft(),
                                            newTop,
                                            insets.getSystemWindowInsetRight(),
                                            insets.getSystemWindowInsetBottom());
                                }

                                // Now apply the insets on our view
                                return ViewCompat.onApplyWindowInsets(v, insets);
                            }
                        });
            } else {
                // Else, we need to use our own FitWindowsViewGroup handling
                ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
                        new FitWindowsViewGroup.OnFitSystemWindowsListener() {
                            @Override
                            public void onFitSystemWindows(Rect insets) {
                                insets.top = updateStatusGuard(insets.top);
                            }
                        });
            }
        }

        if (subDecor == null) {
            throw new IllegalArgumentException(
                    "AppCompat does not support the current theme features: { "
                            + "windowActionBar: " + mHasActionBar
                            + ", windowActionBarOverlay: "+ mOverlayActionBar
                            + ", android:windowIsFloating: " + mIsFloating
                            + ", windowActionModeOverlay: " + mOverlayActionMode
                            + ", windowNoTitle: " + mWindowNoTitle
                            + " }");
        }

        if (mDecorContentParent == null) {
            mTitleView = (TextView) subDecor.findViewById(R.id.title);
        }

        // Make the decor optionally fit system windows, like the window's decor
        ViewUtils.makeOptionalFitsSystemWindows(subDecor);
        //重点
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
        //重点
        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            // There might be Views already added to the Window's content view so we need to
            // migrate them to our content view
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }

            // Change our content FrameLayout to use the android.R.id.content id.
            // Useful for fragments.
            //重点
            windowContentView.setId(View.NO_ID);
            //重点
            contentView.setId(android.R.id.content);

            // The decorContent may have a foreground drawable set (windowContentOverlay).
            // Remove this as we handle it ourselves
            if (windowContentView instanceof FrameLayout) {
                ((FrameLayout) windowContentView).setForeground(null);
            }
        }

        //重点
        // Now set the Window's content view with the decor
        mWindow.setContentView(subDecor);

        contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
            @Override
            public void onAttachedFromWindow() {}

            @Override
            public void onDetachedFromWindow() {
                dismissPopups();
            }
        });

        return subDecor;
    }
    
Copy the code
  • Here AppCompatActivity uses AppCompatDelegateImplV9 to package a layer of laYou to call the PhoneWindow setContentView method and the rest of the Activity flows
  • Notice the following lines of code
  1. Find ContentFrameLayout
  final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
Copy the code
  1. Find android. R.i, dc ontent. This is actually the activity process PhoneWindow generateLayout method to create the layout of the layout id below
    final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
Copy the code
  1. Set the layout name set in the original Activity to NULL
   windowContentView.setId(View.NO_ID);
Copy the code
  1. Setting the current name of the layout to Content will make the parent layout become ContentFrameLayout created by AppCompatActivity, which completes the replacement of the parent layout
      contentView.setId(android.R.id.content);
Copy the code

  • The setContentView method adds the XML layout of its Activty to the parent layout, which is different from an Activity. This wrapper can change the parent layout into the contentParent of an AppCompatActivity
LayoutInflater.from(mContext).inflate(resId, contentParent);
Copy the code

So here’s the summary of the process

The Activity is created by the ActivityThread, calls the attch method to get the ContextImpl, and calls the PhoneWindow setContentView side to create the DecorView. Then add the parent layout to the DecorView and add the XML from your Activty to the parent layout.

AppCompatActivity adds a layer parent layout to an Activity, which can replace the parent of an Activity with a rename and replace operation, and then add the parent layout to the XML of your own Activity

The inflate of a LayoutInflater is the XML that is parsed out, the View is created by reflecting the full path (ImageView, TextView, etc., through loop completion) names and attributes, and the View is added to the root layout

  • Activtiy

  • AppCompatActivity