APK优化之资源缩减

作者: 愿天堂没Android | 来源:发表于2022-03-25 21:25 被阅读0次

    项目开发过程中一切正常,但是打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 #1Error inflating class x

    经过排查发现是因为release包开启资源缩减导致布局文件被移除了,花了点时间看了下关于资源缩减相关的内容。

    资源缩减

    资源缩减需要与代码缩减配合使用才能发挥缩减资源的作用。因为只有在代码缩减器移除所有不使用的代码后,资源缩减器才能真正的确定资源有没有被应用使用,从而确定出哪些是要保留的资源,哪些是要缩减的资源,当您添加包含资源的代码库时尤其需要如此。

    注:资源缩减不会移除value文件下的资源(比如:strings, dimensions, styles, and colors)

    开启资源缩减

    在项目build.gradle文件中添加shrinkResources trueminifyEnabled 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());
    
    

    shrinkModesafe时,我们使用getIdentifier()获取的资源标记为已使用,那么资源就会被保留下来不会被缩减。而shrinkModestrict时,上述方式使用的资源则会判定为未使用,就会被缩减。

    ⭐⭐⭐所以如果您确实启用了严格缩减模式,并且您的代码也通过动态生成的字符串引用资源(如上所示),那么您必须使用 tools:keep 属性中手动保留这些资源。

    我们项目中资源被移除就是因为一个三方库keep.xml文件中设置了shrinkModestrict,而动态生成的字符串引用资源没有配置在keep中,导致资源被缩减了。

    缩减后的资源

    缩减后的资源文件并不会删除,而是内容被移除了,xml文件里面只剩个<x />了。

    这也正是我们项目里面为什么会报错Binary XML file line #1Error 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 会按以下级联优先顺序合并重复资源:

    依赖项 → 主资源 → 构建变种 → 构建类型

    相关文章

      网友评论

        本文标题:APK优化之资源缩减

        本文链接:https://www.haomeiwen.com/subject/jvndjrtx.html