通常我们使用LayoutInflater.from(Context)来获取LayoutInflater类,通过context的GetSystemService方法来获取LayouInflater对象,方法源码如下:(LayoutInflater类中)
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;
}
Context对象是一个抽象类,getSystemService方法是一个抽象方法,因此我们必须要找到它具体实现。在Application、Activity、Service中都会存在一个Context对象,所以Context对象的个数 = Activity的个数+Service的个数+1,布局的加载展示一般都在Activity中,所以下面以Activity的Context对象来分析。
Activity的入口为ActivityThread类的main函数,每次启动一个新的进程的时候都会新建一个ActivityThread实例,具体的Activity启动流程可以参考老罗的《Android系统源代码情景分析》,这里不过多介绍,经过一系列调用之后,我们这里从handleLaunchActivity函数开始:(ActivityThread类中)
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// 重点看这一行
Activity a = performLaunchActivity(r, customIntent);
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//1.创建Activity
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
...
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e);
}
}
try {
// 2. 创建Application对象
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
if (activity != null) {
// 3.获取Context对象
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
...
// 3.attch到Activity中
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window);
...
// 4.调用Activity的OnCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
} catch (SuperNotCalledException e) {
...
}
return activity;
}
第3步中通过createBaseContextForActivity方法创建Context对象,进入这个方法看一下:(ActivityThread类中)
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
...
// 可以看到实现类是ContextImpl
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
...
return appContext;
}
从上面代码可以看到Context的具体实现类为ContextImpl,我们要找到getSystemService方法的具体实现:(ContextImpl类中)
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
可以看到getSystemService方法是通过SystemServiceRegistry类的getSystemService方法来获得,有get方法就一定要有类似put的方法,我们一起进入SystemServiceRegistry类看一下LayoutInflater的注册和获取过程:(SystemServiceRegistry类中)
// 1. 生成缓存的Map容器
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
new HashMap<Class<?>, String>();
// 2.在静态代码块中注册各种各样的service,我们只看LayoutInflater相关的
static {
...
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
...
}
上面第2步的方法就真正生成了LayoutInflater的具体实现类PhoneLayoutInflater实例,后续会讲到这个类,下面来拆解一下第二步所含有的方法和类的实现,首先是registerService方法:(SystemServiceRegistry类中)
// registerService方法将传入的服务名称、服务类、获取类(可缓存服务)传入
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
传入ServiceFetcher的目的是缓存服务,如果服务没有被缓存就创建,缓存过就直接从缓存获取,源码如下:(SystemServiceRegistry类中)
static abstract interface ServiceFetcher<T> {
T getService(ContextImpl ctx);
}
// 实现ServiceFetcher接口,作为抽象类,需要子类实现createService方法,来新建服务
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
private final int mCacheIndex;
public CachedServiceFetcher() {
mCacheIndex = sServiceCacheSize++;
}
// getService方法的具体实现,如果缓存为空则调用createService创建服务,并缓存起来。
@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 {
service = createService(ctx);
cache[mCacheIndex] = service;
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
}
}
return (T)service;
}
}
public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
}
这里通过维护了LayoutInflater单例的形式,到这里我们才分析完LayoutInflater的具体创建过程,这也是大部分服务的创建过程,下面分析LayoutInflater加载布局的源码。
LayoutInflater的加载布局
从上述静态代码块中registerService的方法中我们可以看到CacheServiceFetcher的匿名内部类实现的createService返回的具体实现类为PhoneLayoutInflater类。一般我们新建Activity的时候都会自动加载一个布局,onCreate方法会执行setContentView方法,我们从这里入手,看一下LayoutInflater的具体实现,Activity的setContentView方法源码如下:(Activity类中)
// 内部调用真正调用的getWindow().setContentView(layoutResID);
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
进入之后发现setContentView是一个Window的一个抽象方法,我们又需要找到它的具体实现。这里我们可以回想一下上面分析Activity启动的时候有一个performLaunchActivity方法内部在创建完Activity实例、Application对象和Context对象之后调用了Activity的attach方法,现在我们看一下attach方法:(Activity类中)
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
}
PhoneWindow就是Window的具体实现类,我们看一下真正的setContentView方法:(PhoneWindow类中)
@Override
public void setContentView(int layoutResID) {
...
// 如果mContentPartent为空时构建DectorView
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);
}
...
}
inflate方法有很多重载方法,如下:(LayoutInflater类中)
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
...
// 获取XML解析器
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
// 参数1,传入解析器;参数2,父视图;参数3,是否要将解析的视图加到父视图中
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
...
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
...
final String name = parser.getName();
...
// 判断当前解析是否为merge标签
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
...
// 生成布局参数
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// 如果attachToRoot为false,即不添加到父视图,则给当前布局设置布局参数
temp.setLayoutParams(params);
}
}
...
// 解析temp视图下的所有子视图
rInflateChildren(parser, temp, attrs, true);
...
// 如果有父视图,则添加到父视图中
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// 没有父视图,返回当前结果
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
...
}
return result;
}
}
// rInflate方法如下,通过深度优先遍历,具体逻辑见注释吧。
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
// 获取视图树的深度,
final int depth = parser.getDepth();
int type;
boolean pendingRequestFocus = false;
// 解析节点
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
pendingRequestFocus = true;
consumeChildElements(parser);
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {// 解析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)) { // 判断是否存在merge,不可以作为子标签
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
// 解析子类布局,内部再次调用了rInflate
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if (finishInflate) {
parent.onFinishInflate();
}
}
上面的inflate过程,主要有以下几步:
- 解析xml布局的根标签
- 判断是否为merge标签,如果是则调用rInflate方法进行解析,该方法会将merge标签下面的所有子View直接添加到根标签中
- 不为merge标签,则通过createViewFromTag方法解析
- 调用rInflate解析temp根元素下的所有子View,并且将这些子View都添加到temp下,期间会判断root是否为空和attach是否为false,来确定需要不需要添加到父视图
下面是两个解析的方法源码:(LayoutInflater类中)
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) {
...
try {
View view;
// 开发者可以自定义Factory解析View,默认为空,可忽略
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
// 默认进入下面的代码,通过onCreateView或者createView
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) { // 系统内置控件
view = onCreateView(parent, name, attrs);
} else { // 自定义控件,全包名
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
} catch (InflateException e) {
...
}
}
中间判断是否执行createView的代码即为判断当前解析的控件是否为系统控件,我们可以回想我们使用控件时候,在xml文件中的区别。下面我们来分析这两个方法有何不同:(LayoutInflater类中)
protected View onCreateView(View parent, String name, AttributeSet attrs)
throws ClassNotFoundException {
return onCreateView(name, attrs);
}
protected View onCreateView(String name, AttributeSet attrs)
throws ClassNotFoundException {
return createView(name, "android.view.", attrs);
}
可以看到最终也会进入到createView方法,只不过第二个参数是固定的Android.view.,我们还记得getSystemService方法最终返回的是PhoneLayoutInflater实例,我们看一下这个类的源码:(PhoneLayoutInflater类中)
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app."
};
@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) {
}
}
return super.onCreateView(name, attrs);
}
可以看到这个类扩展了系统内置控件的范围,添加了三个包,将其与的控件直接通过createView方法,createView内部是通过反射的方式创建View对象的,这就是我们使用自定义控件要传入完整的包名的原因,只不过是系统的控件,谷歌帮我们简化了操作而已。下面我们看一下createView的源码:(LayouInflater类中)
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) {
//
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);
}
}
constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
} else {
...
}
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对象
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 (NoSuchMethodException e) {
...
}
}
到此,LayoutInflater的加载过程就分析完了,本代码都是基于api26拷贝的,不同版本可能略有差别,总结个人收获的几点:
- 通过Map集合的形式实现LayoutInflater等多个服务的单例模式
- 使用深度优先遍历视图树
- 通过反射的方式实例化View对象,并区分View和ViewStub
- 解析布局的过程中判断是否为merge标签,来决定是否要直接加载到父布局上
- inflate方法的含义
参考《Android源码设计模式解析与实战》