项目开发过程中一切正常,但是打release包后却发生异常崩溃,捕获到错误日志如下:
the xxxActivity is null: android.view.InflateException: Binary XML file line #1 in [package]:layout/activity_xxx:Binary XML file line #1 in [package]:layout/activity_xxx: Error inflating class x
关键词:Binary XML file line #1
和 Error inflating class x
经过排查发现是因为release包开启资源缩减
导致布局文件被移除了,花了点时间看了下关于资源缩减
相关的内容。
资源缩减
资源缩减
需要与代码缩减
配合使用才能发挥缩减资源的作用。因为只有在代码缩减器
移除所有不使用的代码后,资源缩减器
才能真正的确定资源有没有被应用使用,从而确定出哪些是要保留的资源,哪些是要缩减的资源,当您添加包含资源的代码库
时尤其需要如此。
注:资源缩减不会移除value文件下的资源(比如:strings, dimensions, styles, and colors)
开启资源缩减
在项目build.gradle
文件中添加shrinkResources true
和minifyEnabled true
配置开启资源压缩。
android {
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
自定义要保留的资源
在项目中的res/raw
目录下添加keep.xml
文件,来声明资源缩减的规则。
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="safe"
tools:keep="@layout/activity_*,@drawable/shape_*,@anim/anim_*"
tools:discard="@drawable/xxxx_*_xxxx"/>
keep 和 discard
keep
属性中指定每个要保留的资源。discard
属性中指定每个要舍弃的资源。像上面例子中的值一样,这两个属性都接受以逗号
分隔的资源名称列表,还可以将星号
字符用作通配符。
shrinkMode
shrinkMode
属性有两个枚举值, strict
表示严格引用检查,safe
表示安全引用检查(防患于未然)。这个属性决定了资源缩减器
判定资源是否被使用时的检查严格程度,默认值为safe
。
比如,我们的代码中根据动态生成的字符串查询资源名称,如下:
String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());
shrinkMode
为safe
时,我们使用getIdentifier()
获取的资源标记为已使用,那么资源就会被保留下来不会被缩减。而shrinkMode
为strict
时,上述方式使用的资源则会判定为未使用,就会被缩减。
⭐⭐⭐所以如果您确实启用了严格缩减模式,并且您的代码也通过动态生成的字符串引用资源(如上所示),那么您必须使用 tools:keep
属性中手动保留这些资源。
我们项目中资源被移除就是因为一个三方库keep.xml文件中设置了
shrinkMode
为strict
,而动态生成的字符串引用资源没有配置在keep
中,导致资源被缩减了。
缩减后的资源
缩减后的资源文件并不会删除,而是内容被移除了,xml文件里面只剩个<x />
了。

这也正是我们项目里面为什么会报错
Binary XML file line #1
和Error inflating class x
的原因。
检查被缩减的资源
如何判断是否有必要资源被缩减呢?
在build\outputs\mapping
目录中找到对应的resources.txt 文件,里面记录了资源文件处理情况。
被缩减的资源都会记录在文件底部,Skipped unused被标记,如下图: [图片上传失败...(image-84a02a-1648214733065)]
移除未使用的资源
Gradle 资源缩减器只会移除未由应用代码引用的资源,这意味着,它不会移除用于不同设备配置的备用资源
。如有必要,您可以使用 Android Gradle 插件的 resConfigs
属性移除应用不需要的备用资源文件。
设置只保留英语和法语的语言资源:
android {
defaultConfig {
...
resConfigs "en", "fr"
}
}
合并重复资源
默认情况下,Gradle 还会合并同名的资源,如可能位于不同资源文件夹中的同名可绘制对象。这一行为不受 shrinkResources
属性控制,也无法停用
,因为当多个资源与代码查询的名称匹配时,有必要利用这一行为避免错误。
只有在两个或更多个文件具有完全相同的资源名称、类型和限定符时,才会进行资源合并。
Gradle 会在以下位置查找重复资源:
- 与主源代码集关联的主资源,一般位于
src/main/res/
中。 - 变体叠加,来自构建类型和构建变种。
- 库项目依赖项。
Gradle 会按以下级联优先顺序合并重复资源:
依赖项 → 主资源 → 构建变种 → 构建类型
网友评论