LayoutInflater.from 传参不当引发的android.view.InflateException

3,236 阅读2分钟

一、复现

1.1. 场景

RecyclerView 里面有个楼层使用 android.support.design.widget.TabLayout 做一个 Tab 切使用:

1.2 问题代码出处

RecyclerView.AdapterOnCreateViewHolder简单返回一个ViewHolder:

return new ViewHolder(LayoutInflater
.from(applicationContext).inflate(R.layout.xx, parent, false))

这里我使用的是ApplicationContext,因为我一直被教导不要持有activity的引用,so都是Context应该没有什么不同。

1.3 报错

到楼层展示的时候报错:

android.view.InflateException: 
Binary XML file line #7: 
Error inflating class android.support.design.widget.TabLayout

二、查找问题代码

2.1 XML 检查

最先肯定是检查 xml 文件里面的类路径是否正确,检查后发现没问题。

2.2 代码分析

问题报错解析 TabLayout 类的时出错,打断点,发现在该行代码时崩溃(TabLayout代码在不同版本里相关代码位置会发生微调,本场景是design.28.0.0版本):

public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    ......
    TypedArray a = ThemeEnforcement.obtainStyledAttributes(...);
    ......
}

继续深入:

public static TypedArray obtainStyledAttributes(Context context, AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @StyleableRes int... textAppearanceResIndices) {
    checkCompatibleTheme(context, set, defStyleAttr, defStyleRes);
    .....
}

继续深入:

private static void checkCompatibleTheme(Context context, AttributeSet set, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
    ......
    checkAppCompatTheme(context);
}

继续深入:

public static void checkAppCompatTheme(Context context) {
    checkTheme(context, APPCOMPAT_CHECK_ATTRS, "Theme.AppCompat");
}

问题代码找到

private static void checkTheme(Context context, int[] themeAttributes, String themeName) {
    if (!isTheme(context, themeAttributes)) {
        throw new IllegalArgumentException("The style on this component requires your app theme to be " + themeName + " (or a descendant).");
    }
}

跟主题没找到有关。

三. 分析

1. AppcompatActivity

我们在使用继承该 Activity 时必须使用 Theme.Appcompat 及其子类的主题,不然也会报这个错误。基本问题原因可以定位为LayoutInflater没有带 theme 去解析。

2. Activity Context 与 Application Context 区别

Look at here:LayoutInflater.from参数Context传Activity、Application区别

归根结底是我传入的是 ApplicationContext,解析时没有带 theme 导致崩溃。

四、总结

一直认为 LayoutInflater.from() 传入应用上下文一劳永逸且安全,在大多数场景下没有问题,可以成功解析,但是对于有 theme 要求的控件,还是需要使用 activity 传入解析。