setContentView()流程浅析

314 阅读6分钟

setContentView() 方法 是ActivityonCreate ()方法的第二行,默认是传入一个R.layout.activity_xxx,目的是将我们在xml中写的布局解析渲染到屏幕上进行显示。

setContentView()流程

setContentView()在继承ActivityAppCompatActivity是不同的,我们先来看Activity中的setContentView()

1、Activity#setContentView()

MainActivity继承Activity

class MainActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
    
}

Activity#setContentView()

Activity.java


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);
    mWindow.setCallback(this);
    ...//`省略部分代码`
}


//2、获取Window
public Window getWindow() {
    return mWindow;
}

//1、设置xml
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

  1. Activity#setContentView()是调用getWindow().setContentView(layoutResID)

  2. getWindow()方法返回一个Window对象mWindow

  3. mWindow对象在Activity#attach()方法中实例化,是一个PhoneWindow对象

那么Activity#setContentView(layoutResID)其实就是调用了PhoneWindow#setContentView()

PhoneWindow#setContentView()

PhoneWindow.java

@Override
public void setContentView(int layoutResID) {
       
    if (mContentParent == null) {
        //代码1
        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 {
        //代码2
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

PhoneWindow#setContentView()中做了两件事

  1. 判断mContentParent 为空的时候进入代码1的位置调用方法installDecor()

  2. 执行代码2将传入的xml加载到mContentParent

我们设置的xml加载到了mContentParent中,那么我们需要知道mContentParent是怎么创建的

先看installDecor()

private void installDecor() {
    mForceDecorInstall = false;
    //代码1
    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);
    }
    //代码2
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);

        
        ..省略部分代码
    }
}


protected DecorView generateDecor(int featureId) {
    
    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());
}
installDecor()方法
  1. 代码2 mContentParent = generateLayout(mDecor)可以看出是对mContentParent进行初始化。但是传入了一个mDecor,那这个mDecor是什么

  2. 代码1 generateDecor()是对mDecor的初始化,查看方法generateDecor()返回值是一个DecorView对象。

代码2中的方法generateLayout(mDecor)

public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

protected ViewGroup generateLayout(DecorView decor) {
    ...省略部分代码

    //代码1
    int layoutResource;
    int features = getLocalFeatures();
    
    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();
    //代码2
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    
    //代码3
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }

    if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
        ProgressBar progress = getCircularProgressBar(false);
        if (progress != null) {
            progress.setIndeterminate(true);
        }
    }

    // Remaining setup -- of background and title -- that only applies
    // to top-level windows.
    if (getContainer() == null) {
        mDecor.setWindowBackground(mBackgroundDrawable);

        final Drawable frame;
        if (mFrameResource != 0) {
            frame = getContext().getDrawable(mFrameResource);
        } else {
            frame = null;
        }
        mDecor.setWindowFrame(frame);

        mDecor.setElevation(mElevation);
        mDecor.setClipToOutline(mClipToOutline);

        if (mTitle != null) {
            setTitle(mTitle);
        }

        if (mTitleColor == 0) {
            mTitleColor = mTextColor;
        }
        setTitleColor(mTitleColor);
    }

    mDecor.finishChanging();

    return contentParent;
}

@Nullable
public <T extends View> T findViewById(@IdRes int id) {
    return getDecorView().findViewById(id);
}

@Override
public final @NonNull View getDecorView() {
    if (mDecor == null || mForceDecorInstall) {
        installDecor();
    }
    return mDecor;
}
generateLayout(mDecor)方法
  1. generateLayout(mDecor)方法最后返回一个contentParent,也就是上面的mContentParent

  2. 70行代码3看到contentParent是通过findViewById查找一个ID_ANDROID_CONTENT值为com.android.internal.R.id.content的一个ViewGroup

  3. 114行findViewById()可以看到是DecorView#findViewById去查找的。

  4. 67行代码2处调用mDecor.onResourcesLoaded(mLayoutInflater, layoutResource)传入一个layoutResource

  5. 第6行代码1及后续代码得知layoutResource是一个int,通过features的判断进行不同xml的赋值,最后通过mDecor.onResourcesLoaded方法将xml设置到DecorView中。

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    

    mDecorCaptionView = createDecorCaptionView(inflater);
    
    final View root = inflater.inflate(layoutResource, null);
    
    if (mDecorCaptionView != null) {
        if (mDecorCaptionView.getParent() == null) {
            addView(mDecorCaptionView,
                    new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mDecorCaptionView.addView(root,
                new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
    } else {
        // Put it below the color views.
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
    
    mContentRoot = (ViewGroup) root;
    
    initializeElevation();
}

随机找两个layoutResource 的赋值xml布局查看

R.layout.screen_title.xml

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:fitsSystemWindows="true">
    <!-- Popout bar for action modes -->
    <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:layout_width="match_parent" 
        android:layout_height="?android:attr/windowTitleSize"
        style="?android:attr/windowTitleBackgroundStyle">
        <TextView android:id="@android:id/title" 
            style="?android:attr/windowTitleStyle"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
    
    <FrameLayout android:id="@android:id/content"
        android:layout_width="match_parent" 
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

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>

可以看到两个布局中都有一个id为@android:id/contentFrameLayout, 而上面得知,contentParent是通过findViewById查找一个id为com.android.internal.R.id.content的一个ViewGroup。而这两个id是相等的(笔者不知道原因,有大佬知道可以指导下)。所以contentParent就是这个id为@android:id/contentFrameLayout

此时得知generateLayout(mDecor)中根据不同的features得不同的xml,并把这个xml加载到mDecor也就是DecorView中,这些xml中有一个id为 @android:id/contentFrameLayout,而mContentParent就是这个FrameLayout

回到PhoneWindow#setContentView()

public void setContentView(int layoutResID) {
       
    if (mContentParent == null) {
        //代码1
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        ...
    } else {
        //代码2
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    
}

代码2中将Activity#setContentView()中传入的xml布局,通过mLayoutInflater.inflate(layoutResID, mContentParent)加载到mContentParent中。

总结

  1. Activity#setContentView()其实是调用了PhoneWindow#setContentView()方法;

  2. PhoneWindow#setContentView()方法中调用installDecor()创建DecorView对象mDecor;

  3. generateLayout(mDecor)根据不同的feature加载不同的xml,将xml添加到DecorView中,获取xml中的id为com.android.internal.R.id.contentFrameLayoutmContentParent)通过LayoutInflater#inflate将传入的布局xml加载到mContentParent

最后Activity的层级是下图

image.png

2、AppCompatActivity#setContentView()

MainActivity继承AppCompatActivity

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
    
}

AppCompatActivity.java

@Override
public void setContentView(@LayoutRes int layoutResID) {
    initViewTreeOwners();
    getDelegate().setContentView(layoutResID);
}

@NonNull
public AppCompatDelegate getDelegate() {
    if (mDelegate == null) {
        mDelegate = AppCompatDelegate.create(this, this);
    }
    return mDelegate;
}

AppCompatDelegate.java

@NonNull
public static AppCompatDelegate create(@NonNull Activity activity,
        @Nullable AppCompatCallback callback) {
    return new AppCompatDelegateImpl(activity, callback);
}

AppCompatActivity#setContentView()

AppCompatActivity#setContentView()方法中调用getDelegate().setContentView(layoutResID),而getDelegate()通过AppCompatDelegate.create(this, this)返回一个AppCompatDelegateImpl对象

那么AppCompatActivity#setContentView()最终会执行到AppCompatDelegateImpl#setContentView()

AppCompatDelegateImpl.java

@Override
public void setContentView(int resId) {
    //代码1
    ensureSubDecor();
    //代码2
    ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
    contentParent.removeAllViews();
    //代码3
    LayoutInflater.from(mContext).inflate(resId, contentParent);
    mAppCompatWindowCallback.getWrapped().onContentChanged();
}

AppCompatDelegateImpl#setContentView()

  1. 代码2 contentParentmSubDecor获取一个android.R.id.contentViewGroup;

  2. 代码3通过LayoutInflater#inflat()AppCompatActivity#setContentView()传入的xml加载到contentParent

根据上面分析Activity#setContentView()的流程,mContentParentDecorView中的一个id为com.android.internal.R.id.contentFrameLayout,然后将Activity#setContentView()传入的xml布局通过LayoutInflater#inflat加载到mContentParent

两个的处理方式有点异曲同工

那剩下的我们只需要分析代码1ensureSubDecor()方法

AppCompatDelegateImpl.java

private void ensureSubDecor() {
    if (!mSubDecorInstalled) {
        //初始化mSubDecor
        mSubDecor = createSubDecor();

        ...省略部分代码
    }
}

ensureSubDecor()

ensureSubDecor()方法中通过createSubDecor()创建mSubDecor,继续看createSubDecor()方法

AppCompatDelegateImpl.java

private ViewGroup createSubDecor() {
    ...省略部分代码

    
    //代码2
    ViewGroup subDecor = null;


    if (!mWindowNoTitle) {
        if (mIsFloating) {
            subDecor = (ViewGroup) inflater.inflate(
                    R.layout.abc_dialog_title_material, null);

            ...
        } else if (mHasActionBar) {
            ...
            
            subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                    .inflate(R.layout.abc_screen_toolbar, null);

            mDecorContentParent = (DecorContentParent) subDecor
                    .findViewById(R.id.decor_content_parent);
            ...
    } 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);
        }
    }

    ...省略部分代码

    return subDecor;
}
createSubDecor()

createSubDecor()方法最后返回一个subDecor对象,而subDecor是根据不同的配置加载的不同xml:

R.layout.abc_dialog_title_material

R.layout.abc_screen_toolbar

R.layout.abc_screen_simple_overlay_action_mode

R.layout.abc_screen_simple

下面是四个xml的布局详情

R.layout.abc_dialog_title_material

<?xml version="1.0" encoding="utf-8"?>


<androidx.appcompat.widget.FitWindowsLinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:fitsSystemWindows="true">

    <TextView
            android:id="@+id/title"
            style="?android:attr/windowTitleStyle"
            android:singleLine="true"
            android:ellipsize="end"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="start"
            android:textAlignment="viewStart"
            android:paddingLeft="?attr/dialogPreferredPadding"
            android:paddingRight="?attr/dialogPreferredPadding"
            android:paddingTop="@dimen/abc_dialog_padding_top_material"/>

    <include
            layout="@layout/abc_screen_content_include"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>

</androidx.appcompat.widget.FitWindowsLinearLayout>

R.layout.abc_screen_toolbar

<?xml version="1.0" encoding="utf-8"?>


<androidx.appcompat.widget.ActionBarOverlayLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/decor_content_parent"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

    <include layout="@layout/abc_screen_content_include"/>

    <androidx.appcompat.widget.ActionBarContainer
            android:id="@+id/action_bar_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            style="?attr/actionBarStyle"
            android:touchscreenBlocksFocus="true"
            android:gravity="top">

        <androidx.appcompat.widget.Toolbar
                android:id="@+id/action_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:navigationContentDescription="@string/abc_action_bar_up_description"
                style="?attr/toolbarStyle"/>

        <androidx.appcompat.widget.ActionBarContextView
                android:id="@+id/action_context_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:visibility="gone"
                android:theme="?attr/actionModeTheme"
                style="?attr/actionModeStyle"/>

    </androidx.appcompat.widget.ActionBarContainer>

</androidx.appcompat.widget.ActionBarOverlayLayout>

R.layout.abc_screen_simple_overlay_action_mode

<?xml version="1.0" encoding="utf-8"?>


<androidx.appcompat.widget.FitWindowsFrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/action_bar_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

    <include layout="@layout/abc_screen_content_include" />

    <androidx.appcompat.widget.ViewStubCompat
            android:id="@+id/action_mode_bar_stub"
            android:inflatedId="@+id/action_mode_bar"
            android:layout="@layout/abc_action_mode_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

</androidx.appcompat.widget.FitWindowsFrameLayout>

R.layout.abc_screen_simple

<?xml version="1.0" encoding="utf-8"?>


<androidx.appcompat.widget.FitWindowsLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/action_bar_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true">

    <androidx.appcompat.widget.ViewStubCompat
        android:id="@+id/action_mode_bar_stub"
        android:inflatedId="@+id/action_mode_bar"
        android:layout="@layout/abc_action_mode_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <include layout="@layout/abc_screen_content_include" />

</androidx.appcompat.widget.FitWindowsLinearLayout>

可以看到所有的xml都有include一个@layout/abc_screen_content_include的xml布局

查看abc_screen_content_include.xml 布局中只有 id为 @id/action_bar_activity_contentContentFrameLayout

abc_screen_content_include.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <androidx.appcompat.widget.ContentFrameLayout
            android:id="@id/action_bar_activity_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:foregroundGravity="fill_horizontal|top"
            android:foreground="?android:attr/windowContentOverlay" />

</merge>
private ViewGroup createSubDecor() {
    ...省略部分代码

    // Now let's make sure that the Window has installed its decor by retrieving it
    ensureWindow();
    //代码1
    mWindow.getDecorView();

    final LayoutInflater inflater = LayoutInflater.from(mContext);
    //代码2
    ViewGroup subDecor = null;


    if (!mWindowNoTitle) {
        if (mIsFloating) {
            subDecor = (ViewGroup) inflater.inflate(
                    R.layout.abc_dialog_title_material, null);

            ...
        } else if (mHasActionBar) {
            ...
            
            subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                    .inflate(R.layout.abc_screen_toolbar, null);

            mDecorContentParent = (DecorContentParent) subDecor
                    .findViewById(R.id.decor_content_parent);
            ...
    } 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 (mDecorContentParent == null) {
        mTitleView = (TextView) subDecor.findViewById(R.id.title);
    }

    
    //代码3
    final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
            R.id.action_bar_activity_content);
    //代码4
    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
        //代码5
        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.
        //代码6
        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
    //代码7
    mWindow.setContentView(subDecor);

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

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

    return subDecor;
}

回到createSubDecor()中,为了方便我又复制了一遍源码。

  1. 代码2的地方声明了subDecor对象,然后解析对应的xml,最后返回。

  2. 代码3获取一个id为action_bar_activity_contentcontentView布局,也就是subDecor对应加载xml布局中的abc_screen_content_include.xml中的id为@id/action_bar_activity_contentContentFrameLayout

  3. 代码4中获取了WindowwindowContentView也就是是我们最后加载传入xml的mContentParent,总感觉哪里不对,mContentParent什么时候初始化的,mContentParent有初始化吗?对!!!在代码1mWindow.getDecorView()调用了installDecorView(),然后初始化了mDecormContentParent

image.png

  1. 代码5中通过while循环,将windowContentView中的子View全部添加到代码3contentView中然后在代码6windowContentView的id设置为-1,contentView设置为android.R.id.content,也就是上面AppCompatDelegateImpl#setContentView()方法中的contentParent

image.png

  1. 当然还有最后代码7 通过mWindow.setContentView(subDecor)subDecor设置到mWindowmContentParent

总结

  1. AppCompatActivity#setContentView()最终会执行到AppCompatDelegateImpl#setContentView()

  2. AppCompatDelegateImpl#setContentView()首先调用ensureSubDecor()方法通过createSubDecor()创建mSubDecor

    1. 通过mWindow.getDecorView()初始化DecorViewmContentParent
    2. 根据不同的参数,加载一个包含@layout/abc_screen_content_include的xml
    3. 获取mContentParentabc_screen_content_include中的ContentFrameLayout 然后while遍历把mContentParent添加到ContentFrameLayout中,并设置mContentParent的id为 View.NO_ID 即-1,设置ContentFrameLayoutid为android.R.id.content
    4. 通过mWindow.setContentView(mSubDecor)设置给mContentParent
  3. mSubDecor获取一个id为android.R.id.contentViewGroup contentParent,通过LayoutInflater#inflat()将传入的xml加载到contentParent

image.png