探究 LayoutInflater 源码布局解析原理

LayoutInflater 的作用就是将XML布局文件实例化为相应的 View 对象,需要通过Ac
首页 新闻资讯 行业资讯 探究 LayoutInflater 源码布局解析原理

[[431614]]

本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。

前言

在开发中,对于 LayoutInflater 的 inflate() 方法,它的作用是把 xml 布局转换为对应的 View  对象,我们几乎天天在用;

今天我们就来分析讲解下;

一、什么是LayoutInflater?

LayoutInflater 的作用就是将XML布局文件实例化为相应的 View 对象,需要通过Activity.getLayoutInflater()  或 Context.getSystemService(Class) 来获取与当前Context已经关联且正确配置的标准LayoutInflater;

复制

@SystemService(Context.LAYOUT_INFLATER_SERVICE) public abstract class LayoutInflater {     ... }
  • 1.

  • 2.

  • 3.

  • 4.

获取LayoutInflater

1、 View.inflate(...)

复制

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

  • 2.

  • 3.

  • 4.

2、Activity#getLayoutInflater()

复制

Activity.class的源代码: public class Activity extends .......  { .........  @NonNull     public LayoutInflater getLayoutInflater() {         return getWindow().getLayoutInflater();     } .........  @NonNull     public LayoutInflater getLayoutInflater() {         return getWindow().getLayoutInflater();     } .........    final void attach(.....){           ......            mWindow = new PhoneWindow(this, window, activityConfigCallback);           .......     } ......... } PhoneWindow源码:    public PhoneWindow(Context context) {         super(context);         mLayoutInflater = LayoutInflater.from(context);     }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

3、PhoneWindow#getLayoutInflater()

复制

private LayoutInflater mLayoutInflater; public PhoneWindow(Context context) {     super(context);     mLayoutInflater = LayoutInflater.from(context); } public LayoutInflater getLayoutInflater() {     return mLayoutInflater; }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

4、LayoutInflater#from(Context)

复制

@SystemService(Context.LAYOUT_INFLATER_SERVICE) public abstract class LayoutInflater {  .....  /**      * Obtains the LayoutInflater from the given context.      */     public static LayoutInflater from(Context context) {         LayoutInflater LayoutInflater =                 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);         if (LayoutInflater == null) {             throw new AssertionError("LayoutInflater not found.");         }         return LayoutInflater;     } ..... }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

二、源码分析

1、LayoutInflater#inflate(...)

调用inflate()进行布局解析

复制

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {     final Resources res = getContext().getResources();     1. 解析预编译的布局     View view = tryInflatePrecompiled(resource, res, root, attachToRoot);     if (view != null) {         return view;     }     2. 构造 XmlPull 解析器      XmlResourceParser parser = res.getLayout(resource);     try {     3. 执行解析         return inflate(parser, root, attachToRoot);     } finally {         parser.close();     } }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • tryInflatePrecompiled(...)是解析预编译的布局;

  • 构造 XmlPull 解析器 XmlResourceParser

  • 执行解析,是解析的主流程

2、inflate

复制

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {     1. 结果变量     View result = root;     2. 最外层的标签     final String name = parser.getName();     3. <merge>     if (TAG_MERGE.equals(name)) {         3.1 异常         if (root == null || !attachToRoot) {             throw new InflateException("<merge /> can be used only with a valid "                 + "ViewGroup root and attachToRoot=true");         }         3.2 递归执行解析         rInflate(parser, root, inflaterContext, attrs, false);     } else {         4.1 创建最外层 View         final View temp = createViewFromTag(root, name, inflaterContext, attrs);         ViewGroup.LayoutParams params = null;         if (root != null) {             4.2 创建匹配的 LayoutParams             params = root.generateLayoutParams(attrs);             if (!attachToRoot) {                 4.3 如果 attachToRoot 为 false,设置LayoutParams                 temp.setLayoutParams(params);             }         }         5. 以 temp 为 root,递归执行解析         rInflateChildren(parser, temp, attrs, true);         6. attachToRoot 为 true,addView()         if (root != null && attachToRoot) {             root.addView(temp, params);         }         7. root 为空 或者 attachToRoot 为 false,返回 temp         if (root == null || !attachToRoot) {             result = temp;         }     }     return result; } -> 3.2 void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) {     while(parser 未结束) {         if (TAG_INCLUDE.equals(name)) {             1) <include>             if (parser.getDepth() == 0) {                 throw new InflateException("<include /> cannot be the root element");             }             parseInclude(parser, context, parent, attrs);         } else if (TAG_MERGE.equals(name)) {             2) <merge>             throw new InflateException("<merge /> must be the root element");         } else {             3) 创建 View              final View view = createViewFromTag(parent, name, context, attrs);             final ViewGroup viewGroup = (ViewGroup) parent;             final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);             4) 递归             rInflateChildren(parser, view, attrs, true);             5) 添加到视图树             viewGroup.addView(view, params);         }     } } -> 5. 递归执行解析 final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,             boolean finishInflate) throws XmlPullParserException, IOException {     rInflate(parser, parent, parent.getContext(), attrs, finishInflate); } 3、createViewFromTag createViewFromTag(),它负责由 <tag> 创建 View 对象     View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,             boolean ignoreThemeAttr) {         if (name.equals("view")) {             name = attrs.getAttributeValue(null, "class");         }         // Apply a theme wrapper, if allowed and one is specified.         if (!ignoreThemeAttr) {             final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);             final int themeResId = ta.getResourceId(0, 0);             if (themeResId != 0) {                 context = new ContextThemeWrapper(context, themeResId);             }             ta.recycle();         }         if (name.equals(TAG_1995)) {             // Let's party like it's 1995!             return new BlinkLayout(context, attrs);         }         try {             View view;             if (mFactory2 != null) {                 // ① 有mFactory2,则调用mFactory2的onCreateView方法                 view = mFactory2.onCreateView(parent, name, context, attrs);             } else if (mFactory != null) {                 // ② 有mFactory,则调用mFactory的onCreateView方法                 view = mFactory.onCreateView(name, context, attrs);             } else {                 view = null;             }             if (view == null && mPrivateFactory != null) {                 // ③ 有mPrivateFactory,则调用mPrivateFactory的onCreateView方法                 view = mPrivateFactory.onCreateView(parent, name, context, attrs);             }             if (view == null) {                 // ④ 走到这步说明三个Factory都没有,则开始自己创建View                 final Object lastContext = mConstructorArgs[0];                 mConstructorArgs[0] = context;                 try {                     if (-1 == name.indexOf('.')) {                         // ⑤ 如果View的name中不包含 '.' 则说明是系统控件,会在接下来的调用链在name前面加上 'android.view.'                         view = onCreateView(parent, name, attrs);                     } else {                         // ⑥ 如果name中包含 '.' 则直接调用createView方法,onCreateView 后续也是调用了createView                         view = createView(name, null, attrs);                     }                 } finally {                     mConstructorArgs[0] = lastContext;                 }             }             return view;         } catch (InflateException e) {             throw e;         }      }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

  • 68.

  • 69.

  • 70.

  • 71.

  • 72.

  • 73.

  • 74.

  • 75.

  • 76.

  • 77.

  • 78.

  • 79.

  • 80.

  • 81.

  • 82.

  • 83.

  • 84.

  • 85.

  • 86.

  • 87.

  • 88.

  • 89.

  • 90.

  • 91.

  • 92.

  • 93.

  • 94.

  • 95.

  • 96.

  • 97.

  • 98.

  • 99.

  • 100.

  • 101.

  • 102.

  • 103.

  • 104.

  • 105.

  • 106.

  • 107.

  • 108.

  • 109.

  • 110.

  • 111.

  • 112.

  • 113.

  • 114.

  • 115.

  • 116.

  • 117.

  • 118.

  • 119.

  • 120.

  • 121.

  • 122.

  • 123.

  • 124.

  • createViewFromTag 方法比较简单,首先尝试通过 Factory 来创建View;

  • 如果没有 Factory 的话则通过 createView 来创建View;

3、createView 方法解析

复制

public final View createView(String name, String prefix, AttributeSet attrs)             throws ClassNotFoundException, InflateException {         Constructor<? extends View> constructor = sConstructorMap.get(name);         if (constructor != null && !verifyClassLoader(constructor)) {             constructor = null;             sConstructorMap.remove(name);         }         Class<? extends View> clazz = null;         try {             Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);             if (constructor == null) {                 // Class not found in the cache, see if it's real, and try to add it                 clazz = mContext.getClassLoader().loadClass(                         prefix != null ? (prefix + name) : name).asSubclass(View.class);                 if (mFilter != null && clazz != null) {                     boolean allowed = mFilter.onLoadClass(clazz);                     if (!allowed) {                         failNotAllowed(name, prefix, attrs);                     }                 }                 // ① 反射获取这个View的构造器                 constructor = clazz.getConstructor(mConstructorSignature);                 constructor.setAccessible(true);                 // ② 缓存构造器                 sConstructorMap.put(name, constructor);             } else {                 // If we have a filter, apply it to cached constructor                 if (mFilter != null) {                     // Have we seen this name before?                     Boolean allowedState = mFilterMap.get(name);                     if (allowedState == null) {                         // New class -- remember whether it is allowed                         clazz = mContext.getClassLoader().loadClass(                                 prefix != null ? (prefix + name) : name).asSubclass(View.class);                         boolean allowed = clazz != null && mFilter.onLoadClass(clazz);                         mFilterMap.put(name, allowed);                         if (!allowed) {                             failNotAllowed(name, prefix, attrs);                         }                     } else if (allowedState.equals(Boolean.FALSE)) {                         failNotAllowed(name, prefix, attrs);                     }                 }             }             Object lastContext = mConstructorArgs[0];             if (mConstructorArgs[0] == null) {                 // Fill in the context if not already within inflation.                 mConstructorArgs[0] = mContext;             }             Object[] args = mConstructorArgs;             args[1] = attrs;             // ③ 使用反射创建 View 对象,这样一个 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]));             }             mConstructorArgs[0] = lastContext;             return view;         } catch (ClassCastException e) {         }      }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

createView 方法也比较简单,通过反射来创建的 View 对象;

4、 Factory2 接口

Factory2可以拦截实例化 View 的步骤,在 LayoutInflater 中有两个方法可以设置:

复制

方法1: public void setFactory2(Factory2 factory) {     if (mFactorySet) {         关注点:禁止重复设置         throw new IllegalStateException("A factory has already been set on this LayoutInflater");     }     if (factory == null) {         throw new NullPointerException("Given factory can not be null");     }     mFactorySet = true;     if (mFactory == null) {         mFactory = mFactory2 = factory;     } else {         mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);     } }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

复制

方法2 @hide public void setPrivateFactory(Factory2 factory) {     if (mPrivateFactory == null) {         mPrivateFactory = factory;     } else {         mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);     } }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

使用 setFactory2() 和 setPrivateFactory() 可以设置 Factory2 接口(拦截器),其中同一个  LayoutInflater 的setFactory2()不能重复设置,setPrivateFactory() 是 hide 方法;

总结

  • 通过 XML 的 Pull 解析方式获取 View 的标签;

  • 通过标签以反射的方式来创建 View 对象;

  • 如果是 ViewGroup 的话则会对子 View 遍历并重复以上步骤,然后 add 到父 View 中;

  • 与之相关的几个方法:inflate ——》 rInflate ——》 createViewFromTag ——》 createView ;

  • Factory2 是一个很实用的接口,需要掌握通过 setFactory2() 拦截布局解析的技巧;