系列文章索引
Android系统启动流程
LayoutInflater源码详解
更新
录播回放已上传,请戳链接食用:【Android/源码/面试】LayoutInflater源码详解
前言
这篇文章会从源码的角度分析,LayoutInflater
将xml文件实例化为一个view
对象的流程
我们会发现,其中有两个部分是耗时的主要来源
- XmlResourseParser对xml的遍历
- 反射创建View对象导致的耗时
这两点,又跟Xml的复杂程度成正相关,Xml越复杂,则递归调用所消耗的时间就越长,就产生了我们所说的,卡顿问题
整体流程概览
LayoutInflater的创建
概览
LayoutInflater是一个抽象类,它的创建,并不是交由App层处理的,而是调用了from()
的静态函数,经由系统服务LAYOUT_INFLATER_SERVICE
,最终创建了一个LayoutInflater
的子类对象–PhoneLayoutInflater
重要函数解析
LayoutInflater.from(cxt)
这个函数比较简单,就是根据传递过来的Context对象,调用getSystemService()
来获取对应的系统服务,并赋值给LayoutInflater
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater = //LayoutInflate是一个系统服务,最终返回的是`PhoneLahyoutInflater`
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
而Context
本身是一个抽象类,它真正的实例化对象,是ContextImpl
, 在这个类的getSystemService()
函数中,真正执行获取系统服务的类,是SystemServiceRegistry
,其中又封装了一个ServiceFetcher
来获取真正的系统服务,所有的系统服务,都是存储在一个map集合–SYSTEM_SERVICE_FETCHERS
当中,这里其实是一个get
方法,是从这个map集合中取出对应的系统服务
LayoutInflater
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
SystemServiceRegistry
/**
* Gets a system service from a given context.
*/
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
关于对应的服务的添加,也就是调用了SYSTEM_SERVICE_FETCHERS
的put函数,这个动作是交由registerService()
来完成的
/**
* Statically registers a system service with the context.
* This method must be called during static initialization only.
*/
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);
}
SystemServiceRegistry
这个类中有一个静态代码块,是用来完成所有服务的注册的,这里我们只关心LAYOUT_INFLATER_SERVICE
对应的服务是如何注册的
static{
...
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
...
}
正如我们之前所说,这里最终是创建了一个PhoneLayoutInflater
并返回的,到这里LayoutInflater的创建流程就分析完了
思考
为什么要交由系统服务来做,而不是直接创建一个PhoneLayoutInflater
的实例对象?
LayoutInflater布局的实例化
整体流程
实例化的调用流程我们都很熟悉了,调用layoutInflater
的inflater()
函数,传入一个xml的resId
参数就可以了
重要函数解析
inflate
这个函数就是我们把Xml布局文件实例化为一个View对象的入口了
LayoutInflater
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
View view = tryInflatePrecompiled(resource, res, root, attachToRoot); //这段代码其实是必然返回null的,因为当前版本写死了预编译的Enable为false
if (view != null) {
return view;
}
XmlResourceParser parser = res.getLayout(resource); //获取XmlBlock.Parser对象
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
此处又调用了inflate(parser, root, attachToRoot)
这个函数,来对Xml布局进行解析
这里看到对一些熟悉的标签,比如include
,merge
,的处理,具体细节请看下面的源码及注释
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
//XmlPullParser是一个接口
//此函数是真正执行将xml解析为视图view的过程,此处的parser为根据xml布局获取到的parser对象
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final inflateAttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext =