转载

apk 瘦身系列③:删除没用的资源文件

没用的代码可以删除, 没用的资源文件当然也可以删除。只需要设置 gradle 属性 shrinkResources 为true 即可启用该功能。

build.gradle

Java

android {     ...     buildTypes {         release {             minifyEnabled true             shrinkResources true             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'         }     } }
android {     ...     buildTypes {         release {             minifyEnabledtrue             shrinkResourcestrue             proguardFilesgetDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'         }     } } 

如果有些资源被误删了,在代码中有使用(通过反射),则可以通过 tools:keep 属性来保留这些资源。为了方便管理,还可以创建一个单独的文件来保留所有需要保留的资源,类似于 ProGuard 的配置文件:

res/raw/keep.xml

XHTML

<?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*" />
<?xmlversion="1.0" encoding="utf-8"?> <resourcesxmlns:tools="http://schemas.android.com/tools"   tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" /> 

还可以使用 tools:discard 属性来删除之前保留的属性

res/raw/keep.xml

XHTML

<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools"     tools:shrinkMode="safe"     tools:discard="@layout/unused2" />
<?xmlversion="1.0" encoding="utf-8"?> <resourcesxmlns:tools="http://schemas.android.com/tools"     tools:shrinkMode="safe"     tools:discard="@layout/unused2" /> 

在 tools 网站 上 详细解释了如何通过log 查看资源清理以及 safe 和 strict 清理模式的区别。

当然了,如果你发现有大量的资源文件没有用, 为了保持文件整洁,你最好还是手工自己把这些文件删除掉吧。

使用 ResConfigs 来删除没用的配置种类

各种第三方库都把字符串资源翻译成了各种语言,例如 Support Library 和 Google Play Services 就支持几十种语言。但是在你的应用中可能只支持一种或者几种语言,可以通过 resConfigs 选项来告诉编译器,你的应用只支持这些配置的语言,其他语言资源不会被编译到最终的 apk 文件中:

build.gradle

Java

android {     defaultConfig {         ...         resConfigs "en", "zh"     } }
android {     defaultConfig {         ...         resConfigs "en", "zh"     } } 

上面的配置只保留中文和英文资源文件, 其他资源文件都不会包含在最终的 apk 文件中。

注意,在 resConfigs 中只能定义一种屏幕密度属性,对于不同屏幕密度的处理可以使用后面即将结束的 分屏幕发布不同版本的功能。

resources.arsc 中的松散配置项问题

本节讨论的问题一般是针对大型项目的,这些项目使用了成千上万个资源文件。

如果你发现 resources.arsc 文件占用的空间非常多,多的不太合理,则很有可能就是松散配置项引起的。 下面通过例子来看看引起该问题的原因:

假设在默认的字符串配置文件 (values/strings.xml)中有 5 个字符串。这 5 个字符串的值会定义在一个字符串池中,每个字符串的标识符和对应的池地址会保存在其他地方。那么这 5 个字符串 最后编译到 resources.arsc 中的内容类似如下所示:

Java

String pool: "My App", "Hello", "Exit", "Settings", "Feature"                   Default config:  string/myapp      0x00000001 string/hello      0x00000002 string/exit       0x00000003 string/settings   0x00000004 string/feature    0x00000005
String pool: "My App", "Hello", "Exit", "Settings", "Feature"                   Default config:  string/myapp      0x00000001 string/hello      0x00000002 string/exit      0x00000003 string/settings  0x00000004 string/feature    0x00000005 

现在,假设你在应用中添加了一个新的功能,并且该功能仅仅在 api 21+ 之上存在。该新功能需要显示一个不同的字符串内容,那么你选择在 values-v21/strings.xml 中添加一个字符串。

仅仅添加了一个新的 v21 字符串,则 resources.arsc 的内容将会变成下面这个样子:

Java

String pool: "My App", "Hello", "Exit", "Settings", "Feature", "New feature"                   Default config:         -v21 config: string/myapp      0x00000001              NO_ENTRY string/hello      0x00000002              NO_ENTRY string/exit       0x00000003              NO_ENTRY string/settings   0x00000004              NO_ENTRY string/feature    0x00000005              0x00000006                                      ==========              ========== Config size:      20 bytes                **20 bytes!**
String pool: "My App", "Hello", "Exit", "Settings", "Feature", "New feature"                   Default config:        -v21config: string/myapp      0x00000001              NO_ENTRY string/hello      0x00000002              NO_ENTRY string/exit      0x00000003              NO_ENTRY string/settings  0x00000004              NO_ENTRY string/feature    0x00000005              0x00000006                                      ==========              ========== Configsize:      20 bytes                **20 bytes!** 

每个配置项都占用了所有资源文件的空间,虽然实际上在 v21 中其他字符串指向的地址为 null 但是仍然会占用同样的字节数。每个标识符 4个字节。

如果你的应用中有很多不同的配置项,比如 -v21、 -land 、 -en-land-v21 等。则会多占用不少的空间。

假设在实际场景中,一个应用有 3500 个字符串,但是有一个特殊的字符串单独定义在另外一种配置项中,然后改字符串需要翻译为其他 50 种语言(也就是说将会有类似 values-en-land, -pl-land, -de-land, -fr-land… 等 50个目录),这样会多占用如下的空间:

4 bytes * 3500 null entries * 50 languages = 700 kilobytes

如果你把这个特殊的字符串给删除了,则就可以节省 700KB的空间。 有些大型项目仅仅删除了 3 个资源就可以节省 2.5M 的空间。

针对这种特殊的情况,如果你发现仅仅几个字符串的添加就导致 apk 文件变得很大,则你可以考虑在默认配置项中定义这些特殊的字符串,然后在运行的时候,根据系统配置动态的选择使用哪个字符串。

资源 id 混淆

资源 id 一般也有很长的名字,比如 xxx_xxxx.png ,如果可以像混淆代码一样把资源文件也给混淆了,则同样可以减少 resources.arsc 文件的大小,可以使用 DexGuard 或者 微信团队的 资源混淆器 来实现这个功能。关于资源混淆的原理,可以 参考这里 。

原文  http://blog.chengyunfeng.com/?p=888
正文到此结束
Loading...