当您启用应用优化时,isShrinkResources = true
设置会指示优化器移除未使用的资源,这有助于减小应用的大小。资源收缩仅与代码收缩结合使用时才有效,因此如果您要优化资源,也要设置 isMinifyEnabled = true
,例如:
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
...
}
}
如果您想保留或丢弃特定资源,请在您的项目资源中创建一个 XML“保留”文件,例如 res/raw/my.package.keep.xml
。保留文件包含以下组件:
<resources>
标记 — 包含所有子资源元素和保留/丢弃属性。tools:keep
属性 — 接受以逗号分隔的资源名称列表,用于标识要保留的资源。tools:discard
属性 — 接受以逗号分隔的资源名称列表,用于标识要丢弃的资源。
使用星号字符作为通配符来引用同一文件夹中的多个资源,例如:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
tools:discard="@layout/unused2" />
指定要丢弃的资源可能看起来多余,因为您可以直接删除它们,但丢弃资源在使用构建变体时可能很有用。
定位特定构建变体
要仅在某些构建变体中移除资源,请将所有资源放入通用项目目录,然后为每个构建变体在其资源目录中创建一个不同的 my.package.build.variant.keep.xml
文件。在保留文件中,手动指定当某个资源在代码中看似被使用(因此不会被收缩器移除),但您知道它实际上不会用于给定构建变体时,要移除的资源。
移除未使用的替代资源
优化器只会移除未被应用代码引用的资源,这意味着优化器不会移除针对不同设备配置的替代资源。
在您应用的模块 build.gradle
文件中使用 Android Gradle resConfigs
属性来移除您的应用不需要的替代资源文件。
例如,如果您正在使用包含语言资源(如 Google Play 服务)的库,那么您的应用将包含这些库中所有已翻译的消息语言字符串,无论您的应用其余部分是否已翻译成相同的语言。要仅保留您的应用正式支持的语言,请使用 resConfigs
属性指定这些语言。未指定语言的所有资源都将被移除。
以下代码段展示了如何将您的语言资源限制为仅英语和法语:
android {
defaultConfig {
...
resourceConfigurations.addAll(listOf("en", "fr"))
}
}
或
android {
defaultConfig {
...
resConfigs "en", "fr"
}
}
当您使用 Android App Bundle (AAB) 格式发布应用时,默认情况下,当用户安装应用时,只会下载用户设备上配置的语言。同样,下载中也只会包含与设备屏幕密度匹配的资源和与设备 ABI 匹配的原生库。如需了解更多信息,请参阅重新启用或停用配置 APK 类型。
对于使用 APK(在 2021 年 8 月之前创建)发布的旧版应用,您可以通过构建多个 APK 来针对不同的设备配置,从而自定义要包含在 APK 中的屏幕密度或 ABI 资源。
合并资源时避免冲突
默认情况下,Android Gradle 插件 (AGP) 会合并同名资源,例如在不同资源文件夹中具有相同名称的 drawable。此行为不受 shrinkResources
属性控制,也无法禁用,因为在多个资源具有代码引用的名称时,此行为对于避免错误是必要的。
资源合并仅在两个或更多文件共享相同的资源名称、类型和限定符时发生。AGP 会从重复项中选择其认为的最佳选项(基于下述优先级顺序),并仅将该资源传递给 AAPT,以便在最终构建工件中分发。
AGP 会在以下位置查找重复资源:
- 主资源,与主源集关联,通常位于
src/main/res/
- 变体叠加层,来自构建类型和构建风格
- 库项目依赖项
AGP 按照以下级联优先级顺序合并重复资源:
例如,如果主资源和构建风格中都出现重复资源,Gradle 会选择构建风格中的资源。
如果相同资源出现在同一个源集中,Gradle 无法合并它们并会发出资源合并错误。当您在模块 build.gradle
文件的 sourceSet
属性中定义多个源集时,可能会发生这种情况,例如,如果 src/main/res/
和 src/main/res2/
都包含相同的资源。
解决资源收缩问题
当您收缩资源时,“构建”窗口会显示从应用中移除的资源的摘要。(点击窗口左侧的“切换视图”可显示 Gradle 的详细文本输出。)例如:
:android:shrinkDebugResources
Removed unused resources: Resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning
Gradle 还在 <module-name>/build/outputs/mapping/release/
(与 ProGuard 的输出文件相同的文件夹)中创建一个名为 resources.txt
的诊断文件。该文件包含详细信息,例如哪些资源引用了其他资源,以及哪些资源被使用或移除。
例如,要找出 @drawable/ic_plus_anim_016
为何仍在您的应用中,请打开 resources.txt
文件并搜索该文件名。您可能会发现它被另一个资源引用:
16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out] @drawable/ic_plus_anim_016
您现在需要知道为什么 @drawable/add_schedule_fab_icon_anim
可以访问;如果您向上搜索,您会发现该资源列在 resources.txt
的“可访问的根资源是:”标题下。
这意味着代码中引用了 add_schedule_fab_icon_anim
,也就是说,它的 R.drawable
ID 在可访问的代码中被找到。
除非您使用严格检查,否则如果字符串常量看起来可能用于构造动态加载资源的资源名称,资源 ID 可能会被标记为可访问。在这种情况下,如果您在构建输出中搜索资源名称,您可能会找到如下消息:
10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
used because its format-string matches string pool constant ic_plus_anim_%1$d.
如果您看到其中一个字符串,并且您确定该字符串未用于动态加载给定资源,请在您的保留文件中使用 tools:discard
属性来通知构建系统移除该资源。