LayoutInflater Analysis
什么是 LayoutInflater
LayoutInflater 是 Android 的一個(gè)類,用來將 xml 文件,轉(zhuǎn)換為對應(yīng)的 View 對象。
如何創(chuàng)建 LayoutInflater
官方推薦的兩種獲取 LayoutInflater 實(shí)例的方案:
Activity.getLayoutInflater()Context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
其中第一種方法,我們分析一下源碼
/**
* Convenience for calling
* {@link android.view.Window#getLayoutInflater}.
*/
@NonNull
public LayoutInflater getLayoutInflater() {
return getWindow().getLayoutInflater();
}
可見調(diào)用的是 Window.getLayoutInflater,我們知道,Window 的實(shí)例其實(shí)是 PhoneWindow,因此查看 PhoneWindow 的代碼,發(fā)現(xiàn) PhoneWindow 的 LayoutInflater 的創(chuàng)建在這里:
@UnsupportedAppUsage
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(),
DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
}
而 LayoutInflater.from(context) 的實(shí)現(xiàn)如下:
/**
* Obtains the LayoutInflater from the given context.
*/
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;
}
可見殊途同歸,最后都是調(diào)用 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) 來獲取 LayoutInflater 實(shí)例。
不過到這里,我們還是沒有找到真正創(chuàng)建 LayoutInflater 的地方,于是繼續(xù)跟蹤 context.getSystemService 方法。
Context 的子類有許許多多,對于 Activity 來說,它繼承于 ContextThemeWrapper,于是我們先看 ContextThemeWrapper 的 getSystemService 方法:
@Override
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);
}
可以看到這里做了兩件事,一是將 BaseContext 的LayoutInflater clone 了一份,而是做了個(gè)緩存。
看來要找到真正創(chuàng)建 Inflater 的地方,還得去看這個(gè) BaseContext 的 getSystemService,我們知道,這個(gè)getBaseContext,其實(shí)最終是返回一個(gè) ContextImpl,于是查看 ContextImpl.getSystemService,發(fā)現(xiàn)調(diào)用的是 SystemServiceRegistry.getSystemService 方法:
public static Object getSystemService(ContextImpl ctx, String name) {
if (name == null) {
return null;
}
final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
if (fetcher == null) {
if (sEnableServiceNotFoundWtf) {
Slog.wtf(TAG, "Unknown manager requested: " + name);
}
return null;
}
// Logs
return ret;
}
可見是從 SYSTEM_SERVICE_FETCHERS 獲取出對應(yīng)的 Fetcher,然后拿到真正的實(shí)例,分析后發(fā)現(xiàn),LayoutInflater 也是注冊了一個(gè)這樣的 Fetcher的:
static {
// ...
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
// ...
}
可見創(chuàng)建的是一個(gè) CachedServiceFetcher,作用顧名思義,是做一個(gè)緩存,避免重復(fù)創(chuàng)建,從這里也可以看出,LayoutInflater 真正的實(shí)現(xiàn)類是 PhoneLayoutInflater。
LayoutInflater 原理
LayoutInflater.inflate 時(shí)序圖
skinparam backgroundColor #999999
actor Caller
Caller -> LI : inflate(int res, ViewGroup parent)
LI -> LI : inflate(int, ViewGroup, boolean)
LI -> Resource : getLayout()
LI -> LI : inflate(XmlParser, ViewGroup, boolean)
LI -> LI : advanceToRootNode()
LI -> LI : createViewFromTag(View parent, String name, Context context, AttributeSet attrs) (root element)
LI -> LI : createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr)
LI -> LI : tryCreateView(@Nullable View parent, @NonNull String name, Context context, AttributeSet attrs)
LI -> Factory2 : onCreateView()
LI -> Factory : onCreateView()
LI -> Factory2 : onCreateView()
LI -> LI : createView(String name, String prefix, AttributeSet attrs)
LI -> LI : createView(@NonNull Context viewContext, @NonNull String name, String prefix, AttributeSet attrs) (Use reflect)
LI -> LI : rInflateChildren(XmlParser, root, attrs, true)
LI --> Caller : View
Factory & Factory2
耗時(shí)點(diǎn)
layoutId -> XmlParser
從 LayoutId 到 XmlParser,這里涉及到IO操作
Android resource 加載過程,可參考老羅的博客:
Android應(yīng)用程序資源的查找過程分析
createView
從 XML Tag,生成 View Object,此處用到反射
BenchMark
| Action | Time cost (ms) |
|---|---|
| Inflate a ConstraintLayout 1000 times | 104 |
| Inflate a FrameLayout 1000 times | 66 |
| Inflate a complex layout 1000 times | 1873 |
| Load a complex layout 10 times | 2-3 |
結(jié)論:
對于 Heavy 布局:
load 一次耗時(shí) 0.2 ms
inflater 一次耗時(shí) 1.8ms
可以計(jì)算,create view 的耗時(shí)大概為 1.6ms,大于 Resource Load 耗時(shí)