Android中View的创建流程解析

        在AppCompatActivity的onCreate方法中我们都知道setContentView这个方法是加载布局文件。这个方法使用很简单直接把layout布局文件放进去就可以了。那么具体内部是怎么将它显示到桌面的呢,今天就从setContentView()来解析一下Android中View的创建过程。

打开androidx/appcompat/app/AppCompatActivity.java,这里setContentView就一行代码,调用AppCompatDelegate的setContentView()。

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

AppCompatDelegate是一个抽象类,我们分析一下它的实现类AppCompatDelegateImpl在setContentView()方法中做了什么。

@Override
    public void setContentView(int resId) {
        ensureSubDecor();
        //获取content视图
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        //移除viewGroup下的所有子View
        contentParent.removeAllViews();
        //解析布局文件添加到content视图中
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }

打开/frameworks/base/core/java/android/view/LayoutInflater.java查看解析布局文件的LayoutInflater.inflate() 方法:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        ...
        //获取布局解析器
        XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

...

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);
                //取得XML标签名字
                final String name = parser.getName();
            
                if (TAG_MERGE.equals(name)) {
                    //如果是merge标签调用rInflate方法,递归执行解析
                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // 通过xml的tag来构建View
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;
                    if (root != null) {
                         //获取布局参数
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            //设置临时的布局参数
                            temp.setLayoutParams(params);
                        }
                    }
                    //递归执行解析,继续解析temp下的子项,也走到了rInflate()方法
                    rInflateChildren(parser, temp, attrs, true);
                    // 如果root不为空并且attachToRoot为true
                    if (root != null && attachToRoot) {
                        //将View填充到ViewGroup
                        root.addView(temp, params);
                    }

                    // 如果root为空 或者 attachToRoot为false,直接返回temp
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
              ...
            }

            return result;
        }
    }

在inflate方法中主要做了一下几步:

1.解析layout.xml文件的根标签

2.判断是否是merge,如果是merge那么调用rInflate()递归方法,实例化视图将View添加到ViewGroup中,然后调用onFinishInflate()。

3.如果不是merge,调用createViewFromTag()来创建view并添加到ViewGroup中,之后调用rInflate()继续递归解析子View;

4.通过attachToRoot,返回视图。

rInflate方法同样也调用createViewFromTag方法,打开createViewFromTag():

 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        ...

        try {
            View view = tryCreateView(parent, name, context, attrs);

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                     //通过.来判断是自定义View还是内置View
                    if (-1 == name.indexOf('.')) {
                        //创建内置View
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        //创建自定义View
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }
            return view;
        } ...
          
    }
通过继续跟踪代码,发现自定义View还是内置View,最后都调用了createView方法:
@Nullable
    public final View createView(@NonNull Context viewContext, @NonNull String name,
            @Nullable String prefix, @Nullable AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Objects.requireNonNull(viewContext);
        Objects.requireNonNull(name);
        //从缓存中获取构造
        Constructor<? extends View> constructor = sConstructorMap.get(name);
        if (constructor != null && !verifyClassLoader(constructor)) {
            constructor = null;
            sConstructorMap.remove(name);
        }
        Class<? extends View> clazz = null;

        try {

            if (constructor == null) {
                //类载入器, 初始化对象
                clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                        mContext.getClassLoader()).asSubclass(View.class);

                if (mFilter != null && clazz != null) {
                    boolean allowed = mFilter.onLoadClass(clazz);
                    if (!allowed) {
                        failNotAllowed(name, prefix, viewContext, attrs);
                    }
                }
                //从class中获取构造
                constructor = clazz.getConstructor(mConstructorSignature);
                constructor.setAccessible(true);
                //加入缓存中
                sConstructorMap.put(name, constructor);
            } else {
                ...
            }

            Object lastContext = mConstructorArgs[0];
            mConstructorArgs[0] = viewContext;
            Object[] args = mConstructorArgs;
            args[1] = attrs;

            try {
                //设置attrs属性,通过反射获取View实例
                final View view = constructor.newInstance(args);
                if (view instanceof ViewStub) {
                    // Use the same context when inflating ViewStub later.
                    final ViewStub viewStub = (ViewStub) view;
                    viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
                }
                return view;
            } finally {
                mConstructorArgs[0] = lastContext;
            }
        } 
        ...
    }
  • 总结:

  • 通过 pull解析XML文件获取到 View 的标签;
  • 通过标签中是否有.判断是自定义View还是内置View
  • 通过反射的方式来创建 View 对象;
  • 通过深度优先的顺序遍历View, 形成View树;

Guess you like

Origin blog.csdn.net/weixin_43858011/article/details/124584373