View——View创建的流程

一、了解LayoutInflater

public static LayoutInflater from(Context context) {
    LayoutInflater LayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
    //省略异常捕捉的代码..
    return LayoutInflater;
}

由此可知LayoutInflater的实例是通过getSystemService(Context.LAYOUT_INFLATER_SERVICE)获取到的, 接下来看看是如何获取到的

    /**
     * ContextImpl.getSystemService
     */
    @Override
    public Object getSystemService(String name) {
        // 1. 调用了 SystemServiceRegistry 的静态方法, invokestatic 指令会触发类的初始化
        // 即会先初始化这个 SystemServiceRegistry 类中的静态代码块
        return SystemServiceRegistry.getSystemService(this, name);
    }
    
   /**
    * 2. 在静态代码块中注册服务
    * SystemServiceRegistry.static{...}
    */
    registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
            new CachedServiceFetcher<LayoutInflater>() {
        @Override
        public LayoutInflater createService(ContextImpl ctx) {
            return new PhoneLayoutInflater(ctx.getOuterContext());
        }
    });

    /**
     * SystemServiceRegistry.registerService
     */
    private static <T> void registerService(String serviceName, Class<T> serviceClass,
            ServiceFetcher<T> serviceFetcher) {
        // 3. 将静态代码块中注册的系统服务 put 到集合中缓存, 保证进程间的唯一性
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }

    /**
     * SystemServiceRegistry.getSystemService
     */
    public static Object getSystemService(ContextImpl ctx, String name) {
        // 4. 从缓存中取
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }
    
   

上面的源码分析的很透彻, Andriod 7.0 中的源码, 是通过 SystemServiceRegistry 完成了相应的操作

二、View构建的流程

/**
 * View.inflate
 */
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
    LayoutInflater factory = LayoutInflater.from(context);
    return factory.inflate(resource, root);
}

/**
 * LayoutInflater.inflate
 */
public View inflate(int resource, ViewGroup root) {
    return inflate(resource, root, root != null);
}

/**
 * LayoutInflater.inflate
 * <p>
 * @param resource:为我们定义的xml布局(R.layout.xxx)
 * @param root:可用于存放我们xml布局的父容器
 * @param attachToRoot:该boolean变量为是否将我们xml布局加入root中
 */
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
    XmlResourceParser parser = getContext().getResources().getLayout(resource);
    try {
        inflate(parser, root, attachToRoot);
    } finally {
        parser.close();
    }
}
/**
 * LayoutInflater.inflate
 * <p>
 * @param parser: 为xml解析器
 * @param root: 可用于存放我们xml布局的父容器
 * @param attachToRoot: 该boolean变量为是否将我们xml布局加入root中
 */
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot);

分析了源码之后可以得出如下的等价关系:

  1. View.inflate(context, R.layout.xxx, viewGroup/null) ->
    LayoutInflater.from(context).inflate(R.layout.xxx, viewGroup/null)

  2. LayoutInflater.from(context).inflate(R.layout.xxx, viewGroup/null) ->
    LayoutInflater.from(context).inflate(R.layout.xxx, viewGroup/null, true/false)

由上述的源码可知: 无论我们调用哪一种inflate方法,最终都会交由
inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) 来执行

接下来就来分析一下这个 inflate 方法做了哪些事情

    /**
     * 从指定的 XML 节点填充一个新的 View 层级
     *
     * @param parser XML dom node containing the description of the view
     *        hierarchy.
     * @param root Optional view to be the parent of the generated hierarchy (if
     *        <em>attachToRoot</em> is true), or else simply an object that
     *        provides a set of LayoutParams values for root of the returned
     *        hierarchy (if <em>attachToRoot</em> is false.)
     *        if (attachToRoot == true) root 则作为解析 xml 所生成 View 的父类
     *        if (attachToRoot == false) root 则会为解析 xml 所生成的 View 生成一个 LayoutParams 
     * @param attachToRoot Whether the inflated hierarchy should be attached to
     *        the root parameter? If false, root is only used to create the
     *        correct subclass of LayoutParams for the root view in the XML.
     * @return The root View of the inflated hierarchy. If root was supplied and
     *         attachToRoot is true, this is root; otherwise it is the root of
     *         the inflated XML file.
     */
    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            // 这个 mConstructorArgs 为创建 View 实例的构造函数的参数数组
            // 记录之前 mConstructorArgs[0] 中保存的 Context
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            // 给 result 赋初值
            View result = root;

            try {
                // while 循环查找 xml 中第一个有效的节点标签
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // 什么也没做...
                }
                // 解析到最后 type 仍然是 START_TAG 说明当前 XML 没有起始节点则扔出异常
                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }
                final String name = parser.getName();
                // 判断解析到的根节点是否为 Merge 标签
                if (TAG_MERGE.equals(name)) {
                    // 若为 merge 标签则必须保证传入的 root 不为空, 且 attachToRoot 为 true
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid ViewGroup root and attachToRoot=true");
                    }
                    // 1. 若 xml 跟 View 为 <Merge> 标签并且通过了上面的验证, 则调用 rInflate 方法
                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // 2. 若 xml 不为根节点的情况, 调用 createViewFromTag 来找到并创建根节点的 View
                    // Temp 是 xml 根节点的 View
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;
                    if (root != null) {
                        // 从 xml 文件解析的属性集合中获取 temp 的 LayoutParams
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // 若不需要添加到 root 中则为其绑定 LayoutParams
                            temp.setLayoutParams(params);
                        }
                    }
                    
                    // 以 temp 为父 ViewGroup 遍历 temp 的子孩子, 添加到对应的父容器中
                    rInflateChildren(parser, temp, attrs, true);
                    
                    // 判断是否需要将解析出来的 temp 添加到 root 中
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }
                    // 若满足以下条件, 则直接返回从 xml 解析出来的 temp(View)
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }
            } catch (XmlPullParserException e) {
            } catch (Exception e) {
            } finally {
                // 将 mConstructorArgs[0] 还原成之前的 lastContext, 防止内存泄漏
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;
            }
            return result;
        }
    }
    
    /**
     * LayoutInflater.rInflate
     */
    void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
        // 获取当前 parser 所处的深度
        final int depth = parser.getDepth();
        int type;
        boolean pendingRequestFocus = false;

        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
            // 忽略 TAG
            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 {
                // 1.1 可以看到同样是通过 createViewFromTag 来创建View, 可见它非常重要
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                // 1.2 这里同样也调用了 rInflateChildren 方法来解析 view 的子孩子
                rInflateChildren(parser, view, attrs, true);
                // 添加到 parent 中去
                viewGroup.addView(view, params);
            }
        }
        // 等待获取焦点
        if (pendingRequestFocus) {
            parent.restoreDefaultFocus();
        }
        // 是否结束 inflate 流程
        if (finishInflate) {
            parent.onFinishInflate();
        }
    }


    /**
     * LayoutInflater.rInflateChildren
     */
    final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
        // 1.2.1 递归调用 rInflate 方法, 这样就将解析到的 View 一层一层的都添加到各自的 parent 中去了
        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
    }

好了, 是时候总结一下了, 代码量还是有些多的, 不过思路很清晰, 不得不叹服 Google 的技巧, inflate 方法主要做了如下事情:

  1. 解析 XML 文件, 找到第一个有效的起始节点
  2. 起始节点为 <Merge> 标签
    • 验证是否满足 (root != null && attachToRoot) 的条件, 不满足则抛出异常, 这也说明了 Merge 不能作为根 View 单独存在
    • 调用了 rInflate 方法去创建 Xml 中的 View 的实例对象, 且添加到各自对应的 parent 中
      • 调用了 createViewFromTag 创建View
      • 调用 rInflateChildren 进行递归
  3. 起始节点不为 <Merge> 标签
    • 直接通过 createViewFromTag 来创建 View 实例对象 temp
    • 接着调用了 rInflateChildren, 以 temp 为父 ViewGroup 遍历 temp 的子孩子, 添加到对应的父容器中

通过 inflate 方法后, View 的树形结构就成功的构建起来了

好! 重点来了, View是怎么创建的, 我们继续追溯 createViewFromTag 这个方法

    /**
     * LayoutInflater.createViewFromTag
     */
    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        // ... 省略部分代码
        try {
            View view;
            // 1. 判断用户是否给LayoutInflater 添加了 Factory/Factory2
            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, context, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }
            // 2. 调用内部的私有工厂去创建 View 实例
            // 这个 mPrivateFactory(Factory2) 在 LayoutInflater 的 Constructor 中被创建
            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }
            // 3. 调用 LayoutInflater 内部自己定义的方法去执行 View 的创建
            if (view == null) {
                // 记录之前的 Context
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    // 判断解析到的节点名称中是否带有 ".", 没有 "." 则说明是系统控件
                    if (-1 == name.indexOf('.')) {
                        // 3.1 调用 onCreateView 来创建系统 View
                        view = onCreateView(parent, name, attrs);
                    } else {
                        // 3.2 调用 createView 来创建自定义 View
                        view = createView(name, null, attrs);
                    }
                } finally {
                    // 还原为之前的 Context
                    mConstructorArgs[0] = lastContext;
                }
            }
            return view;
        } catch (InflateException e) {
        } catch (ClassNotFoundException e) {
        } catch (Exception e) {
        }
    }
    
    /**
     * LayoutInflater.onCreateView
     */
    protected View onCreateView(View parent, String name, AttributeSet attrs)
            throws ClassNotFoundException {
        // 可见这个传入的 parent 完全无用武之地, 可能是 Google 故意在这里留了一层, 提升拓展的可能性
        return onCreateView(name, attrs);
    }
    
    /**
     * LayoutInflater.onCreateView
     */
    protected View onCreateView(String name, AttributeSet attrs)
            throws ClassNotFoundException {
        // 这个比较有意思, 它给系统控件拼接了一个前缀, 最终还是回调了 createView 来创建View
        return createView(name, "android.view.", attrs);
    }
    

createViewFromTag 这个方法还是比较有意思的, 它做了如下的事情:

  1. 优先使用 Factory2 创建 View
  2. 次优先使用 Factory 创建 View
  3. 次次优先使用 LayoutInflater.mPrivateFactory 对象来创建 View
  4. 如果上述方式都无法创建 View
    • LayoutInflater.onCreateView() 创建系统View, 最终还是调用 create 方法
    • LayoutInflater.createView() 创建自定义View

好了, 终于快走到终点了, 最后来看看 createView 方法

    /**
     * LayoutInflater.createView
     */
    public final View createView(String name, String prefix, AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        // 1. 通过名字从缓存中获取View的构造函数
        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) {
                // 2. 若缓存中没有 View 的 Constructor, 尝试通过 ClassLoader 去加载这个 View 的 Class
                // prefix+ name 拼接 Class 的全限定名
                clazz = mContext.getClassLoader().loadClass(
                        prefix != null ? (prefix + name) : name).asSubclass(View.class);
                // 3. 通过 Class 获取构造函数, 加入缓存
                constructor = clazz.getConstructor(mConstructorSignature);
                constructor.setAccessible(true);
                sConstructorMap.put(name, constructor);
            }
            // 记录之前的 Context 
            Object lastContext = mConstructorArgs[0];
            if (mConstructorArgs[0] == null) {
                // 构造方法的第 0 个参数为 Context
                mConstructorArgs[0] = mContext;
            }
             // 构造方法的第 1 个参数为 AttributeSet
            Object[] args = mConstructorArgs;
            args[1] = attrs;
            // 4. 终于看到了这个方法反射构造对象
            final View view = constructor.newInstance(args);
            // 5. 若为 ViewStub 对象则懒加载
            if (view instanceof ViewStub) {
                // Use the same context when inflating ViewStub later.
                final ViewStub viewStub = (ViewStub) view;
                viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
            }
            // 还原成之前 Context
            mConstructorArgs[0] = lastContext;
            // 返回回去
            return view;
        } catch (NoSuchMethodException e) {
        } catch (ClassCastException e) {
        } catch (ClassNotFoundException e) {
        } catch (Exception e) {
        } finally {
        }
    }

好了分析到这儿 View 创建的流程就比较明了了, 最终无非是反射创建对象, 从代码中也可以看到很多细节和值得学习的地方

    原文作者:SharryChoo
    原文地址: https://www.jianshu.com/p/5d330a746b35
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞