APK编译错误之Error inflating class

这里记录的案例,虽说确实是编译原因导致的,但问题真正发生是在运行时。开发工具:Android Studio 3.4.1,运行环境:Android 5.1.1。

问题现象

话说为了选择个文件,使用了第三方库com.leon:lfilepickerlibrary:1.8.0,由于这个库和原来的项目同时引用了com.android.support:appcompat-v7,但版本不一致,可能出现Duplicate class的问题,所以,使用排除传递依赖项的方法,排除了这个库对com.android.support的引用,关于Duplicate class的问题参考这里。在初始的build.gradle里具有这样的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
compileSdkVersion 28
defaultConfig {
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation('com.leon:lfilepickerlibrary:1.8.0') {
exclude group: 'com.android.support'
}
}

编译一切正常,安装到手机上运行并使用这个库提供的功能时,应用出现停止运行,查看logcat,具有如下内容:

1
2
3
4
5
java.lang.RuntimeException: Unable to start activity ComponentInfo{.../com.leon.lfilepickerlibrary.ui.LFilePickerActivity}: android.view.InflateException: Binary XML file line #62: Error inflating class com.leon.lfilepickerlibrary.widget.EmptyRecyclerView}
...
Caused by: android.view.InflateException: Binary XML file line #62: Error inflating class com.leon.lfilepickerlibrary.widget.EmptyRecyclerView
...
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.leon.lfilepickerlibrary.widget.EmptyRecyclerView" on path: DexPathList...

解决过程

  1. ClassNotFoundException看起,使用JadX反编译了生成的apk,发现com.leon.lfilepickerlibrary.widget.EmptyRecyclerView确实存在于apk内,路径也完全一致,看来不是这个问题。

  2. 在网上搜索Apk ClassNotFoundExceptionApk Error inflating class看了一些相关的内容后,发现都和这里的不同。

  3. 不得已,回到项目重新思考引用这个库的方式,怀疑可能是依赖存在问题,于是做了以下尝试:

    首先,删除了exclude group: 'com.android.support'这一段,这时,Android Studio会在implementation 'com.android.support:appcompat-v7:28.0.0'这一行上会显示红色波浪线,鼠标点击后,即时提示告知:“All com.android.support libraries must use the exact same version specification (mixing versions can lead to runtime crashes). Found versions 28.0.0, 25.0.0.”。可见,这个第三方库引用了com.android.support 25.0.0的版本。

    其次,根据提示,把compileSdkVersion、targetSdkVersion、com.android.support:appcompat-v7的版本都从28改到了25后,编译并运行,发现功能正常。

  4. 推断依赖存在问题,那么,com.leon:lfilepickerlibrary:1.8.0的依赖到底应该是怎样的呢?查看这个包的pom文件就可以知道了,在本地缓存里(一般是当前用户主目录的.gradle/caches目录下)搜索到对应的pom文件,查看依赖节具有如下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <dependencies>
    <dependency>
    <groupId>com.android.support</groupId>
    <artifactId>appcompat-v7</artifactId>
    <version>25.0.0</version>
    <scope>compile</scope>
    </dependency>
    <dependency>
    <groupId>com.android.support</groupId>
    <artifactId>recyclerview-v7</artifactId>
    <version>25.0.0</version>
    <scope>compile</scope>
    </dependency>
    </dependencies>

    这里的内容显示:除了appcompat-v7模块外,这个库还依赖于recyclerview-v7模块,而使用exclude group: 'com.android.support'排除了所有groupId是com.android.support的模块。

  5. 了解了原因后,解决起来就简单了,compileSdkVersion、targetSdkVersion、com.android.support:appcompat-v7仍使用28版本,但是在build.gradle的dependencies节里增加一行implementation 'com.android.support:recyclerview-v7:28.0.0'。这里,recyclerview-v7的版本应当和appcompat-v7的版本完全一致。最终,build.gradle中的dependencies按照下面的写法即可:

1
2
3
4
5
6
7
8
9
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation('com.leon:lfilepickerlibrary:1.8.0') {
exclude group: 'com.android.support'
}
}
  1. 最后,这些模块都可以通过浏览https://maven.google.com找到,也可以在Android Studio中找到并添加,操作路径是:选中相应的Project,按F4打开Project Structure,点击左侧的Dependencies,然后添加Library Dependency,在出现的对话框中输入com.android.support并搜索,搜索结果出来后,选中recyclerview-v7对应的版本即可添加,具体如下图:

Add Library Dependency

Add Search Result

除此之外,不直接添加对recyclerview-v7的引用,而是仅排除可能产生冲突的模块也是可以的,使用exclude时指定module即可,在这里,就是如下列所示:

1
2
3
implementation('com.leon:lfilepickerlibrary:1.8.0') {
exclude group: 'com.android.support', module: 'appcompat-v7'
}

但是这会导致引用了不同版本的com.android.support组的模块,因此,并不推荐这种方式。