(一)高级UIsetContentView流程分析

201 阅读3分钟

一、Activity的setContentView流程

1.Activity.java中的setContView方法,首先调用getWindow()也就是PhoneWindow

image.png

2.getWindow()方法需要找activity的启动流程,ActivityThread.performLaunchActivity()方法

1)通过仪表类反射获取到activity对象

image.png 2)调用activity的attach方法,其里面就创建了PhoneWindow。

image.png

image.png

3)调用仪表类的callActivityOnCreate方法。

image.png

3.PhoneWindow的setContentView方法

1)调用installDecor()方法,创建DecorView(就是个frameLayout)拿到ContentParent(就是ViewGroup,xml布局上的一个布局view)。

image.png

image.png

  • generateDecor(-1)方法就是创建了DecorView()对象 image.png
  • generateLayout(mDecor)

-- 获取到xml里的属性设置信息,设置上去。

-- 把根据属性选则的xml布局添加到DecorView上。

-- 获取布局上的控件

image.png

2)把.xml渲染到Content上

image.png

二、布局结构

image.png

image.png

三、 AppCompatActivity的流程

1.AppCompatActivity的setContentView()调用的是AppCompatDelegateIml的setContentView()

AppCompatActivity.java image.png

AppCompatDelegate.java image.png

AppcompatDelegateIml.java image.png

2.ensureSubDector()方法。

主要就是调用creatSubDecor().

3.creatSubDecor()方法。

  • 获取xml配置的属性

  • 获取PhoneWindow.(在oncreat里已经获取到了)

  • 通过phoneWindow调用getDecorView()方法与上面的代码开始相同。

      1)创建DecorView
      2) 获取到xml里的属性设置信息,设置上去。
      3)把根据属性选则的xml布局添加到DecorView上。
      4)获取布局上的控件
    
  • 创建subDecor的ViewGroup,并添加布局来替换DecorView中的Content,修改subDecor内的控件ID与DecorView中的viewGroup相同。(这里说的viewGroup是为了放activity的xml的)

image.png PhoneWindow.java

image.png

四、LayoutInflater.from(mContext).inflate(id,布局,boolean)的解析。

1.LayoutInflate.inflate()

解析xml布局文件,为后续创建各个控件布局做准备。

LayoutInflate.java image.png

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

        final Context inflaterContext = mContext;
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = inflaterContext;
        View result = root; //创建根布局

        try {
            advanceToRootNode(parser);
            final String name = parser.getName();

            if (DEBUG) {
                System.out.println("**************************");
                System.out.println("Creating root view: "
                        + name);
                System.out.println("**************************");
            }
            //判断xml的根标签是不是merge标签。
            if (TAG_MERGE.equals(name)) {
                //如果是,没有root或者attachToRoot为false,抛异常崩溃
                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);

                ViewGroup.LayoutParams params = null;
                //root不为空
                if (root != null) {
                    if (DEBUG) {
                        System.out.println("Creating params from root: " +
                                root);
                    }
                    // Create layout params that match root, if supplied
                    //获取到父布局的参数。
                    params = root.generateLayoutParams(attrs);
                    //如果attachToRoot为false
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        //把根布局的参数,传递给新创建的根视图。
                        temp.setLayoutParams(params);
                    }//否则不传入。
                }

                if (DEBUG) {
                    System.out.println("-----> start inflating children");
                }

                // Inflate all children under temp against its context.
                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.
                //root布局不为Null,attachToRoot,则调用addView()方法
                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;
                }
            }

        } catch (XmlPullParserException e) {
            final InflateException ie = new InflateException(e.getMessage(), e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        } catch (Exception e) {
            final InflateException ie = new InflateException(
                    getParserStateDescription(inflaterContext, attrs)
                    + ": " + e.getMessage(), e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        } finally {
            // Don't retain static reference on context.
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;

            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        //返回根布局
        return result;
    }
}

2.创建子视图,通过递归的方式

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)) {
        //include标签不能是root
            if (parser.getDepth() == 0) {
                throw new InflateException("<include /> cannot be the root element");
            }
            parseInclude(parser, context, parent, attrs);
        } else if (TAG_MERGE.equals(name)) {
        //如果子布局中有merge,则抛出异常。
            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();
    }
}

二、面试题目

1.requestWindowFeature(Window.FEATURE_NO_TITLE)要放在哪里?为什么?

答案:需要放在setContentView上面。

调用的是getWindow().requestFeature(featureId);

image.png

在PhoneWindow的setContentView最下面设置成了true. image.png

2. PhoneWindow什么时候会创建

下面的创建都会创建一个PhoneWdindow

  • Activity
  • Dialog
  • PopupWindow
  • Toast

3.添加布局的写法个结果。

image.png

4. merge、include、viewstub的区别。

merge:只能当root_view,作用优化布局。

include:不能做root_view,include的布局,id会被替换成include上的id。

viewstub:根include差不多,可以懒加载,默认隐藏。