探究 LayoutInflater 源码布局解析原理

本文转载自微信公众号「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 {      ... } 

获取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); } 

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);     } 

3、PhoneWindow#getLayoutInflater()

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

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、源码原理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();     } }  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)) {              // Lets party like its 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;         }      }  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 its 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) {          }      } 

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);     } }  方法2 @hide public void setPrivateFactory(Factory2 factory) {      if (mPrivateFactory == null) {          mPrivateFactory = factory;     } else {          mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);     } } 

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

总结

通过 XML 的 Pull 解析方式获取 View 的标签; 通过标签以反射的方式来创建 View 对象; 如果是 ViewGroup 的话则会对子 View 遍历并重复以上步骤,然后 add 到父 View 中; 与之相关的几个方法:inflate ——》 rInflate ——》 createViewFromTag ——》 createView ; Factory2 是一个很实用的接口,需要掌握通过 setFactory2() 拦截布局解析的技巧;
滇ICP备2023000592号-31