美文网首页
解密setContentView

解密setContentView

作者: 喜健_staringOrz | 来源:发表于2020-06-01 23:30 被阅读0次
    56e2-iufmpmn9741169.png

    我们先来看一下Android中View视图在Activity中的整个层级关系:

    androidView视图.png

    包含关系: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开发

    qrcode_for_gh_8ae4a07bf7b8_258.jpg

    相关文章

      网友评论

          本文标题:解密setContentView

          本文链接:https://www.haomeiwen.com/subject/swaezhtx.html