Android之解密setContentView

解密setContentView

我们先来看一下Android中View视图在Activity中的整个层级关系:
在这里插入图片描述

包含关系:Activity中有个成员变量Window,Window是个抽象类,它的实现类是PhoneWindow,PhoneWindow有一个成员变量DecorView.

Phonewindow对象创建的开始

简要说一下整个调用流程:

入口:ActivityThread#handleLaunchActivity() ->ActivityThread#performLaunchActivity()

开始创建PhoneWindow : performLaunchActivity() -> Activity.attach()

后续的DecorView的创建主要在SetContentView中,后面会分析到.

// Activity类
final void attach(...) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        // 创建PhoneWindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ... 
}

setContentView登场

学过Android的都知道,setContentView通常会在Acvitity中的Oncreate方法中调用,那么SetContentView到底做了什么呢?

DecorView的创建

DecorView创建来龙去脉 Activity#setConTentView -> PhoneWindow#setConTentView -> PhoneWindow#install -> PhoneWindow#generateDecor

代码调用链如下:

// Activity类
public void setContentView(@LayoutRes int layoutResID) {
        // 获取PhoneWindow对象,调用PhoneWindow中的SetContentView方法
        getWindow().setContentView(layoutResID);
        // 初始化ActionBar
        initWindowDecorActionBar();
    }
    
// PhoneWindow类
@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) {
            // 创建DecorView对象实例
            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);
        }
      ...
    }
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 {
            mDecor.setWindow(this);
        }
        ...
}
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();
        }
        // 返回创建的DecorView对象,至此DecorView创建完毕
        return new DecorView(context, featureId, this, getAttributes());
    }
    

LayoutInflater.inflate(layoutResID, mContentParent)做了什么

参数:

  • 第一个参数layoutResId:资源文件id(对应xxx.xml)

  • 第二个参数mContentParent: viewGroup对象,表示创建的view位于viewGroup下

逻辑处理过程如下:

// LayoutInflater类
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    // 第三个参数表示创建的view是否依附于viewGroup下
    return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }
        // 解析xml,返回XmlResourceParser对象,通过该对象可以读取xml中的view定义
        final XmlResourceParser parser = res.getLayout(resource);
        try {
            // 通过XmlResourceParser创建出一个view层级(一棵view数)
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            ...
            ...
            // 获取xml中所有的属性
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;
            // Temp is the root view that was found in the xml
            // 创建,取得当前xml的根view
            final View temp = createViewFromTag(root, name, inflaterContext, attrs);
            ...
            ...
            ...
            // Inflate all children under temp against its context.
            // 创建根view下的子view
            rInflateChildren(parser, temp, attrs, true);
            // 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);
            }
            // Decide whether to return the root that was passed in or the
            // top view found in xml.
            if (root == null || !attachToRoot) {
                result = temp;
            }
            return result;
        }
    }

final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
    }

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 {
                // 此处递归创建子view,最终会得到一棵view树
                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();
        }
    }

总结一下 LayoutInflater.inflate一共做了什么:

解析xml得到XmlPullParser对象 -> 通过XmlPullParser对象创建出viewRoot -> 把创建处理的view添加到父view中 -> 递归创建子view并把创建处理的view添加到父view中,最后得到一棵viewTree

以上就是SetContentView的所有过程
更多Android分享关注公众号:Android开发
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章