(一)前言
印象最深刻是在Adapter的getView方法中,经常会这样写代码:
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_vip, parent, false);
基本没想过原理是什么,只知道最后item_vip会在设备屏幕上显现就是了。知其然不知其所以然,对于立志成为更好的自己来说,这可不是个好现象哦。我们今天就来细细研究一下LayoutInflater相关源码。
我们先单纯的看上面的代码,LayoutInflater的from方法输入了一个xml参数文件,然后输出了View对象。即可以说LayoutInflater的主要功能来说就是布局加载。
回想我们写Activity经常用以下代码:
setContentView(R.layout.activity_main);
其实最终也是用LayoutInflater来加载布局的。总不能我说什么就是什么吧,看代码。
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
再点getWindow的setContentView方法:
public abstract void setContentView(@LayoutRes int layoutResID);
发现是抽象方法,而getWindow()指向什么?我们在onCreate中打个Log看看。
Log.d("ala", String.valueOf(getWindow()));
原来Window的子类PhoneWindow实现了setContentView方法。找到PhoneWindow看看里面怎么写的:
public void setContentView(int layoutResID) {
//省略部分代码
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
我追踪了上面的if语句,最终也是调用inflate方法。在这我就不在这分析了,这部分大家自己看代码确认。
还有一点,如果你的Activity继承的是AppCompatActivity,那么点击onCreate方法中setContentView,跳到的代码会有所不同,但是模仿我上述的操作,还是得到想要的信息的。
(二)LayoutInflater的form()方法分析
找到LayoutInflater类,查看其from方法:
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;
}
点进getSystemService()方法,发现是个abstract抽象方法。那就得找哪个类实现了这个方法,当然是Activity类啦。点进Activity的getSystemService方法:
public Object getSystemService(@ServiceName @NonNull String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
//大兄弟我们走这!
return super.getSystemService(name);
}
因为我们传的参数name值是Context.LAYOUT_INFLATER_SERVICE,所以直接走return super.getSystemService(name);
跳到了ContextThemeWrapper类中的getSystemService方法中:
public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
//俺是个单例耶~~
if (mInflater == null) {
//再多看我两眼~
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}
程序会走第一个if语句,我们可以看到每个Activity内包含的LayoutInflater是一个单例。最开始mInflater肯定是null,所以又回到了刚开始的LayoutInflater.from()方法:
LayoutInflater.from(getBaseContext()).cloneInContext(this);
不一样的是这次上下文的对象是ContextImpl对象,为什么是ContextImpl呢?看明白我Context及其子类源码分析(一)文章中关于“Activity中ContextImpl实例化源码分析”的人就会知道:每个Activity是在ActivityThread类中的performLaunchActivity方法中开始被创建的,紧接着被实例的activity会实例一个ContextImpl对象,通过attach方法,最终将这个ContextImpl对象赋给Activity的父类ContextWrapper(Activity实际继承了ContextThemeWrapper ,而ContextThemeWrapper 继承自ContextWrapper )中的成员变量mBase对象。上面代码中的getBaseContext()就是指的mBase对象。其实我们可以因此想到,创建了不同的Activity,那他们的mBase也是不同的,即不同Context对象创建的LayoutInflater对象也不同。
我们知道一开始mInflater为null,所以又走刚开始的刚开始的LayoutInflater.from()方法,但这个时候,context指的是明确了的ContextImpl,那么去ContextImpl类中看getSystemService方法[1]:
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;
}
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
查找SystemServiceRegistry类的getSystemService()方法:
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
我们再查一下fetcher.getService的相关代码:
static abstract interface ServiceFetcher {
T getService(ContextImpl ctx);
}
static abstract class CachedServiceFetcher implements ServiceFetcher {
private final int mCacheIndex;
public CachedServiceFetcher() {
mCacheIndex = sServiceCacheSize++;
}
@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) {
service = createService(ctx);
cache[mCacheIndex] = service;
}
return (T)service;
}
}
public abstract T createService(ContextImpl ctx);
}
我们可以看到逻辑上会先去缓存中查找ContextImpl中有没有service,如果没有就createService新建一个。而createService是个抽象方法,找到它的实现类SystemServiceRegistry:
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
终于是感觉快到头了,最终是实例了PhoneLayoutInflater这么一个对象,即mInflater为null时候,逻辑上最终LayoutInflater.from(getBaseContext())这句话是创建了一个PhoneLayoutInflater实例,但是我们仔细看下前面的代码是这样的:
LayoutInflater.from(getBaseContext()).cloneInContext(this);
cloneInContext()这个方法是做什么的呢?
/**
* Create a copy of the existing LayoutInflater object, with the copy
* pointing to a different Context than the original. This is used by
* {@link ContextThemeWrapper} to create a new LayoutInflater to go along
* with the new Context theme.
*
* @param newContext The new Context to associate with the new LayoutInflater.
* May be the same as the original Context if desired.
*
* @return Returns a brand spanking new LayoutInflater object associated with
* the given Context.
*/
public abstract LayoutInflater cloneInContext(Context newContext);
说的是通过现有的LayoutInflater创建一个新的LayoutInflater副本,唯一变化的地方是指向不同的上下文对象。就是说LayoutInflater.from(getBaseContext())创建了一个PhoneLayoutInflater实例,然后cloneInContext方法根据这个实例又克隆了一个新的。
至此,我们简单地将LayoutInflater类的from方法逻辑走了一遍,后续内容我们再在下一篇文章中继续分析。
[1]ContextImpl类有的人通过AS可能看不到,这里推荐一个网站查看源码:ContextImpl.java