本文主要讲述app如何在onCreate方法中调用setContentView来加载布局,对于想知道app从启动到执行setContentView之间的执行逻辑的小伙伴可以查看Android加载流程解析(从app启动到执行onCreate)。

首先查看setContentView方法

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

从名字可以看出,1执行了视图的加载,2初始化了ActionBar,查看1,可以追踪到PhoneWindowsetContentView方法:

public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor(); //1
    } 
    ...
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
		...
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent); //2
    }
	...
}

从名字可以推断1执行了一些初始化的工作,2才是真正动态加载视图的操作,inflate动态加载layoutResIDroot视图mContentParent中,那么**这个mContentParent如何来的,是一个什么样的视图?**留着这个疑问先继续往下查看1都做了什么工作:

private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor(-1);
		...
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        ...
    }
    ...
}

这里需要关注mDecormContentParent两个对象

// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;

查看mDecor的描述,可以看出这是window的顶层视图,注意DecorView继承自FrameLayout,也是一个ViewGroup

// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
ViewGroup mContentParent;

查看mContentParent的描述,可以看出这是视图存放的父类布局,可以是顶层布局mDecor自己,也可以是下面的子视图

protected ViewGroup generateLayout(DecorView decor) {
    ...
    int layoutResource;
    // 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) 		{
		...
    }
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
	...
    return contentParent;
}

中间省略了很多layoutResource的赋值逻辑,从上面可以看出来,mDecor调用onResourcesLoaded来展示顶层布局,查看onResourcesLoaded

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

该方法动态加载layoutResource布局,同时将获得的view(root)绑定到自身(DecorView),mDecorCaptionView也只是rootDecorView的中间层,本质上root绑定到了DecorView

通过mDecor.onResourcesLoaded(mLayoutInflater, layoutResource)mDecor渲染了基本布局,该布局中存在一个id为contentFrameLayout容器(这个容器就用来放置apk视图的区域,平时我们新建项目的main_acvitity就是放在这里),如下图

android setContent android setcontentview 解析_android


执行完onResourcesLoaded后,PhoneWindow新建了contentParent

//ID_ANDROID_CONTENT: com.android.internal.R.id.content
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

到这恍然大悟了,原来这个contentParent就是apk视图的父视图,最后generateLayout返回该父视图并赋值给mContentParent

mContentParent = generateLayout(mDecor)

接下来就是对DecorView中的一些默认视图做一些初始化操作,就不过多叙述了,跳出installDecor,查看下一个关键点

mLayoutInflater.inflate(layoutResID, mContentParent)

到这就很明确了,前面的installDecor就是确认了全局根视图,并做了初始化的操作,同时返回apk视图的位置mContentParent(ViewGroup),接下来的任务就是动态将apk的视图(layoutResID)动态加载并绑定到对应的父容器中(mContentParent