简介
驱使我去了解LayoutInflater的原动力是五月份在写热插件demo时,总是会在资源加载出一些乱起糟的异常,所以就把资源从文件加载到内存的整个流程熟悉了一边,顺带也解决了刚入门android时,对setContentView是如何将xml转换成View的好奇。
正文
从activity的setContentView开始
先看下setContentView(int res)的源码(android 5.1):
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();
}
使用过DecorView的都应该挺熟悉getWindwow这个方法的,返回的是一个Window对象的引用,实例是PhoneWindow,进入PhoneWindow去看下setContentView:
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
这里的前几行并不需要我们关心,installDecor是对DecorView进行初始化使用的,FEATURE_CONTENT_TRANSITIONS这是专场动画标记,最关心的其实是mLayoutInflater.inflate(layoutResID,mContentParent);
这里有两个关键点:
1、mLayoutInflater是从哪里来的?
2、inflate方法是如何处理资源id,并将其以view的形式加入到mContentParent(mContentParent的英文解释:This is the view in which the window contents are placed. It is either mDecor itself, or a child of mDecor where the contents go.)
1、mLayoutInflater是从哪里来的?
在源码中找到mLayoutInflater被赋值的位置:
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
mLayoutInflater在PhoneWindow中被初始化,那么from方法又是如给mLayoutInflater进行赋值的呢,继续往下看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就充满了熟悉感,毕竟对系统服务的调用就是通过getSystemService进行实现的,那么就进 ContextImpl(Context的实现类)中一探究竟:
@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
关于ServiewFetcher不做过多描述,可以当作一个生成service的简单工厂,源码有兴趣的话可以去看一下。
这里需要注意的是这个SYSTEM_SERVIEC_MAP:
private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP =
new HashMap<String, ServiceFetcher>();
在翻下是在哪里进行的属性添加:
private static void registerService(String serviceName, ServiceFetcher fetcher) {
if (!(fetcher instanceof StaticServiceFetcher)) {
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
}
SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}
static {
......
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}});
......
}
在这里可以看到ServiceFetcher是在类加载之后就立马加入到map中的,那么想要再往走,就要进入到PolicyManager中:
public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static {
// Pull in the actual implementation of the policy at run-time
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
} catch (InstantiationException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
}
}
// Cannot instantiate this class
private PolicyManager() {}
// The static methods to spawn new policy-specific objects
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
public static LayoutInflater makeNewLayoutInflater(Context context) {
return sPolicy.makeNewLayoutInflater(context);
}
public static WindowManagerPolicy makeNewWindowManager() {
return sPolicy.makeNewWindowManager();
}
public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
return sPolicy.makeNewFallbackEventHandler(context);
}
}
PolicyManager类就是个简单工厂中的工厂类,其接口类是IPolicy,实现类是Policy,有代码也可以看的,Policy是通过类加载器进行加载并初始化的,所以真正的逻辑代码还在Policy中:
public PhoneLayoutInflater makeNewLayoutInflater(Context context) {
return new PhoneLayoutInflater(context);
}
在这里只是简单的创建了一个PhoneLayoutInflater对象,这就有意思了,我们要的明明是LayoutInflater,那这个类必然是其子类,猜一下想必构造方法也所差无几:
public class PhoneLayoutInflater extends LayoutInflater {
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app."
};
/**
* Instead of instantiating directly, you should retrieve an instance
* through {@link Context#getSystemService}
*
* @param context The Context in which in which to find resources and other
* application-specific things.
*
* @see Context#getSystemService
*/
public PhoneLayoutInflater(Context context) {
super(context);
}
protected PhoneLayoutInflater(LayoutInflater original, Context newContext) {
super(original, newContext);
}
/** Override onCreateView to instantiate names that correspond to the
widgets known to the Widget factory. If we don't find a match,
call through to our super class.
*/
@Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
for (String prefix : sClassPrefixList) {
try {
View view = createView(name, prefix, attrs);
if (view != null) {
return view;
}
} catch (ClassNotFoundException e) {
// In this case we want to let the base class take a crack
// at it.
}
}
return super.onCreateView(name, attrs);
}
public LayoutInflater cloneInContext(Context newContext) {
return new PhoneLayoutInflater(this, newContext);
}
}
此类重写了onCreatView方法,作用就是在onCreatView之前便利webkit、app、widget这几个包下是否有对应的View存在,至于初始化什么的自然和LayoutInflater一样了,也就是说我们在这里可以把它就看作是一个标准的LayoutInflater!
这样就清楚了,虽然过程有点绕,但实际上就相当于在activty中新建了一个LayoutInflater,然后通过inflate方法将res_id转换成相应的View加入到对应的容器布局(mContentView)中。