PS:本文系转载文章,阅读原文可读性会更好,文章末尾有原文链接
ps:本篇文章是基于 Android Api 26 来分析的
目录
1、LayoutInflater 创建过程
2、LayoutInflater 创建 View 过程
在 Android 中,LayoutInflater 一般叫做为布局解析器或者又叫填充器,LayoutInflater 就是将 XML 布局文件实例化为对应的 View 对象,LayoutInflater 不能直接通过 new 的方式获取,需要通过 Activity.getLayoutInflater() 或 Context.getSystemService() 获取与当前 Context 已经关联且正确配置的标准 LayoutInflater。
1、LayoutInflater 创建过程
在系统中,我们获取 LayoutInflater 对象的方式有2种,一种是通过 LayoutInflater 的 from(Context context) 方法获得;一种是通过 Activity 的 getLayoutInflater方法获得,通过 Activity 的 getLayoutInflater方法获得的 LayoutInflater 最终还是调用 LayoutInflater 的 from(Context context) 方法,如何证明?我们从 Activity 的 getLayoutInflater方法入手看看;
@NonNull
public LayoutInflater getLayoutInflater() {
return getWindow().getLayoutInflater();
}
Activity 的 getWindow 方法得到的是 Window 类型的对象,而 Window 的实现类是 PhoneWindow(要想知道 Window 的实现类是 PhoneWindow 可以看Android中View事件的分发第一篇这篇文章),所以我们往下看 PhoneWindow 的 getLayoutInflater方法;
@Override
public LayoutInflater getLayoutInflater() {
return mLayoutInflater;
}
可以看到 PhoneWindow 的 getLayoutInflater 方法直接返回 mLayout-Inflater 字段,那 mLayoutInflater 字段是在哪里赋值的呢?它被赋值的地方只有一次,那就是 PhoneWindow 的构造方法 PhoneWindow(Context context) ;
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
从这里可以证明出,通过 Activity 的 getLayoutInflater方法获得的 LayoutInflater 最终还是调用 LayoutInflater 的 from(Context context) 方法。
好了,我们现在开始进行分析 LayoutInflater 的创建过程,我们来看看 LayoutInflater 的 from(Context context) 方法;
public static LayoutInflater from(Context context) {
//1、
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
注释1 这里,用 Context 的 getSystemService(@ServiceName @NonNull String name) 方法来创建 LayoutInflater 对象,但是 Context 的 getSys-temService(@ServiceName @NonNull String name) 方法是一个抽象方法,而在于它的实现类上,这里的 context 可以是 Activity、Application 和 Service 中的一个实例,我们就以 context 是 Activity 实例化的来进行分析,但在 Activity 中还是没有重写getSystemService(@ServiceName @NonNull String name) 方法,只是在 Activity 的父类 ContextWrapper 进行了重写;
@Override
public Object getSystemService(String name) {//2、
return mBase.getSystemService(name);
}
注释2 中的 mBase 是 Context 类型的对象,它的实现类是 ContextImpl(要想知道为什么mBase 的实现类是 ContextImpl,可以看Android中Application、Activity和Service它们真正干活的Context是什么?这篇文章),所以我们看 ContextImpl 类的 getSystemService(String name) 方法;
@Override
public Object getSystemService(String name) {//3、
return SystemServiceRegistry.getSystemService(this, name);
}
看注释3,ContextImpl 类的 getSystemService(String name) 方法调用了 SystemServiceRegistry 的 getSystemService(ContextImpl ctx, String name) 方法;
public static Object getSystemService(ContextImpl ctx, String name) {
//4、
ServiceFetcher> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
//5、
return fetcher != null ? fetcher.getService(ctx) : null;
}
我们看注释4 的代码,特别是 SYSTEM_SERVICE_FETCHERS 这个常量,它是个 HashMap 类型的,只是看到它通过 key 为 name 而取出 value 为 ServiceFetcher 的操作,那它又是在哪里保存的呢?我们看一下 SystemServiceRegistry 的静态代码块;
final class SystemServiceRegistry {
......
static {
......
//6、
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
......
}
......
}
看注释6 的代码,name 为 Context.LAYOUT_INFLATER_SERVICE,这里把 CachedServiceFetcher(并重写 createService(ContextImpl ctx) 方法返回 PhoneLayoutInflater 对象) 对象作为参数之一并调用 SystemServiceRegistry 的 registerService 方法(参数比较多,我这不方便列举出来,具体看下面的代码);
private static
ServiceFetcher serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
//7、
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
看注释7 的代码,当 serviceName 为 Context.LAYOUT_INFLAT-ER_SERVICE 时,SYSTEM_SERVICE_FETCHERS 保存的是 CachedServiceFetcher 对象;回到注释4 的代码,fetcher 变量不为空,且本质是 CachedServiceFetcher 对象;再看注释5 的代码,也就是 CachedServiceFetcher(该类是 SystemServiceRegistry 的内部类) 的 getService(ContextImpl ctx) 方法;
static abstract class CachedServiceFetcher
......
@Override
@SuppressWarnings("unchecked")
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
synchronized (cache) {
// Fetch or create the service.
Object service = cache[mCacheIndex];
if (service == null) {
try {//8、
service = createService(ctx);
cache[mCacheIndex] = service;
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
}
}
return (T)service;
}
}public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
}
CachedServiceFetcher 的 getService(ContextImpl ctx) 方法返回的对象是通过 CachedServiceFetcher 的 createService(ContextImpl ctx) 方法创建的,上面分析注释6 的代码的时候就已经说过 CachedServiceFetcher 对象创建后就重写了 createService(ContextImpl ctx) 方法返回 PhoneLayoutIn-flater 对象,所以 PhoneWindow 创建的 LayoutInflater 本质上是创建 PhoneLayoutInflater 对象。
我们知道,通过 Activity、Application 和 Service 都能拿到一个 PhoneLayoutInflater 对象,它们拿到的 PhoneLayoutInflater 对象是有区别的吗?答案是有区别的,Application 和 Service 拿到的是同一个 PhoneLayoutInflater 对象;而每一个 Activity 拿到的是不同的 PhoneLayoutInflater 对象,更别说 Activity 和它们(Application 和 Service)的拿到的是同一个对象了。好了,我们先分析 Application 和 Service 拿到的 PhoneLayoutInflater 对象,我们知道 Application 和 Service都继承于 ContextWrapper,在 ContextWrapper 的 getSystemService(String name) 方法并没有做特殊处理(看注释2 的代码),都是用 ContextImpl 的 getSystemService(String name) 方法调用 SystemServiceRegistry 的静态方法 getSystemService(ContextImpl ctx, String name) 拿到同一个 CachedServiceFetcher 对象,而且 CachedServiceFetcher 重写的 createService(ContextImpl ctx) 方法只会被调用一次,因为只有 service为空的时候才会被调用(看注释8 的代码),创建好 PhoneLayoutInflater 对象后将其保存到 cache 数组中,所以Application 和 Service 拿到的是同一个 PhoneLayoutInflater 对象。
但是 Activity 就不一样了,Activity 继承了 ContextThemeWrapper 并让 ContextThemeWrapper 重写了 getSystemService(String name)方法;
@Override
public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {//9、
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}
看注释9 的代码并通过前面的分析,LayoutInflater.from(getBaseContext()) 这行代码本质上拿到的是 PhoneLayoutInflater 对象,然后调用 PhoneLayoutInflater 的 cloneInContext(Context newContext)方法;
public LayoutInflater cloneInContext(Context newContext) {
//10、
return new PhoneLayoutInflater(this, newContext);
}
看见注释10 的代码没有,每个 Activity 拿到的 PhoneLayoutInflater 都是重新创建的,我们再往下追踪 PhoneLayoutInflater的构造器;
protected PhoneLayoutInflater(LayoutInflater original, Context newContext) {//11、
super(original, newContext);
}
看注释11 的代码,这里的 super 表示 LayoutInflater,我们再跟踪父类 LayoutInflater的构造器;
protected LayoutInflater(LayoutInflater original, Context newContext) {
mContext = newContext;
mFactory = original.mFactory;
mFactory2 = original.mFactory2;
mPrivateFactory = original.mPrivateFactory;
setFilter(original.mFilter);
}
参数 original 是应用进程级的 LayoutInflater,即在 SystemServiceRegistry 中保存,并把应用进程级的配置给了Activity 的 LayoutInflater ,这也符合了 LayoutInflater 的自我介绍 “ 且是正确配置的标准 LayoutInflater ”。
2、LayoutInflater 创建 View 过程
在日常开发中,我们也会用到 LayoutInflater来创建一个 View ,常写的语句如下所示;
LayoutInflater.from(context).inflate(R.layout.item,root,false);
首先 LayoutInflater.from(context) 拿到的肯定是 PhoneLayoutInflater对象,我们来看一下 PhoneLayoutInflater 的 inflate 方法(该方法在 LayoutInflater 里实现,因为 PhoneLayoutInflater 继承于 LayoutInflater,还有一个是该方法参数太多,我不方便写,具体参数看下面的代码,我又给它另起名 method1);
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
......
try {
//12、
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
看注释12 的代码,PhoneLayoutInflater 的 inflate 方法(method1)又调用重载的另一个 inflate 方法(该方法也是在 LayoutInflater 里实现,参数不一样,同时参数太多不方便写,具体参数看下面的代码,我又给它另起名 method2);
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
......
//13、
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
//14、
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}//15、
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
......
//16、
if (TAG_MERGE.equals(name)) {//17、
if (root == null || !attachToRoot) {
throw new InflateException(" can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}//18、
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
//19、
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
//20、
if (root != null) {
......
// Create layout params that match root, if supplied
//21、
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
//22、
temp.setLayoutParams(params);
}
}
......
// Inflate all children under temp against its context.
//23、
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.
//24、
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.
//25、
if (root == null || !attachToRoot) {
result = temp;
}
}} catch (XmlPullParserException e) {
......
} catch (Exception e) {
......
} finally {
......
}return result;
}
}
分析上面的 PhoneLayoutInflater 的 inflate 方法(method2)之前,我们先给出2张图;
图片
图1
图片
图2
注释13 的代码表示获取 xml 布局文件的属性;注释14 的代码表示保存根视图,最终会将根视图返回;注释15 的代码表示如果找不到根标签,那么就会抛出异常;注释19 的代码表示创建具体的 View;注释21 的代码表示如果根视图 root 不为空,那么通过 root 创建对应的 LayoutParams 对象;注释22 的代码表示如果创建的 View 不需要添加到根视图 root 中,那么直接设置 View 的 LayoutParams;注释22 的代码表示如果根视图 root 不为空且需要添加到 root 中,那么就将创建好的 View 添加到 root 里面;注释25 的代码表示如果根视图 root 为空且不需要添加根视图里面,那么就用第一次创建的 View 作为根视图。
下面我们举个例子,再详细解释一下,假设图1 的布局文件叫 layout_1.xml,图2 的布局文件叫 layout_2.xml,root 为 Activity 里的 setContentView 方法里面的布局文件里的一个 ViewGroup,那么我已知的3个东西创建一个 View,看下面代码咯(下面代码的 this,我假设它是 Activity);
//26、
View view = LayoutInflater.from(this).inflate(R.layout.layout_1,root,false);
//27、
View view2 = LayoutInflater.from(this).inflate(R.layout.layout_1,root,true);
//28、
View view3 = LayoutInflater.from(this).inflate(R.layout.layout_1,null,false);
//29、
View view4 = LayoutInflater.from(this).inflate(R.layout.layout_2,root,true);
看注释26 的代码中的 inflate 方法(method2),由于 layout_1.xml 文件中根标签不是 merge,而是 RelativeLayout ,所以会执行注释19 的代码,由于 root 不为空,attachToRoot 为 false,所以也会执行注释20、21、22 的代码,注释26 的代码执行完后最终返回 root。
看注释27 的代码中的 inflate 方法(method2),由于 layout_1.xml 文件中根标签不是 merge,而是 RelativeLayout ,所以会执行注释19 的代码,由于 root 不为空,attachToRoot 为 true,所以会执行注释20、21 、24的代码,注释27 的代码执行完后最终返回 root。
看注释28 的代码中的 inflate 方法(method2),由于 layout_1.xml 文件中根标签不是 merge,而是 RelativeLayout ,所以会执行注释19 的代码,由于 root 为空,attachToRoot 为 false,所以会执行注释25的代码,注释28 的代码执行完后最终返回 RelativeLayout 对象。
看注释29 的代码中的 inflate 方法(method2),由于 layout_2.xml 文件中根标签是 merge,所以会执行注释16 的代码,注释16 的代码表示判断是否是 merge 标签,这里的 root 不能为空,且 attachToRoot 要为true,不然会执行注释17 的代码引发报错,29 的代码执行完后最终返回 root。
注释18 的代码和注释23 的代码本质上都是解析子标签,只是参数不一样而已,都是调用同一个方法;本篇文章先写到这里,由于还没有分析完,注释18、23 的代码分析,下一篇文章见。
【Android中的LayoutInflater分析(一)】本篇文章写到这里就结束了,由于技术水平有限,文章中难免会出错,欢迎大家批评指正。
推荐阅读
- Android中的LayoutInflater分析(二)
- Android黑科技(如何启动未注册的Activity)
- Android中Application、Activity和Service它们真正干活的Context是什么()