Android LayoutInflater 原理簡析

LayoutInflater Analysis

什么是 LayoutInflater

LayoutInflater 是 Android 的一個(gè)類,用來將 xml 文件,轉(zhuǎn)換為對應(yīng)的 View 對象。

如何創(chuàng)建 LayoutInflater

官方推薦的兩種獲取 LayoutInflater 實(shí)例的方案:

  1. Activity.getLayoutInflater()
  2. 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í)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容