码个蛋(codeegg) 第 945 次推文
作者:jsonchao链接:
复习上篇:《深入探索 Android 包瘦身(上)》
资源瘦身方案探索
众所周知,Android构建工具链中使用了AAPT/AAPT2工具来对资源进行处理,Manifest、Resources、Assets 的资源经过相应的 ManifesMerger、ResourcesMerger、AssetsMerger 资源合并器将多个不同 moudule 的资源合并为了 MergedManifest、MergedResources、MergedAssets。然后,它们被 AAPT 处理后生成了 R.java、Proguard Configuration、Compiled Resources。如下图左上方所示:
其中 Proguard Configuration、Compiled Resources的作用如下所示:
- Proguard Configuration:这是AAPT工具为Manifest中声明的四大组件与布局文件中使用的各种Views所生成的混淆配置,该文件通常存放在
${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/proguard-rules/${flavorName}/${buildType}/aapt_rules.txt
。 - Compiled Resources:它是一个Zip格式的文件,这个文件的路径通常为
${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/res/resources-${flavorName}-${buildType}-stripped.ap_
。在经过zip解压之后,可以发现它包含了res、AndroidManifest.xml和resources.arsc 这三部分。并且,从上面的APK构建流程中可以得知,Compiled Resources会被apkbuilder打包到APK包中,它其实就是APK的资源包。因此,我们可以通过 Compiled Resources 文件来修改不同后缀文件资源的压缩方式来达到瘦身效果的。但是需要注意的是,resources.arsc 文件最好不要压缩存储,如果压缩会影响一定的性能,尤其是在冷启动时间方面造成的影响。并且,如果在 Android 6.0 上开启了 android:extractNativeLibs=”false” 的话,So 文件也不能被压缩。
1、冗余资源优化
1、使用 Lint 的 Remove Unused Resource
APK的资源主要包括图片、XML,与冗余代码一样,它也可能遗留了很多旧版本当中使用而新版本中不使用的资源,这点在快速开发的App中更可能出现。我们可以通过点击右键,选中Refactor,然后点击Remove Unused Resource => preview可以预览找到的无用资源,点击Do Refactor可以去除冗余资源。如下图所示:
需要注意的,Android Lint 不会分析 assets 文件夹下的资源,因为 assets 文件可以通过文件名直接访问,不需要通过具体的引用,Lint 无法判断资源是否被用到。
2、优化 shrinkResources 流程真正去除无用资源
resources.arsc中可能会存在很多无用的资源映射,我们可以使用 android-arscblamer,它是一个命令行工具,能够解析 resources.arsc 文件并检查出可以优化的部分,比如一些空的引用。
此外,当我们通过 shrinkResources true来开启资源压缩,资源压缩工具只会把无用的资源替换成预定义的版本而不是移除。那么,如何高效地对无用资源自动进行去除呢?
我们可以 在 Android 构建工具执行 package${flavorName}Task 之前通过修改 Compiled Resources 来实现自动去除无用资源,具体的实现原理如下:
1)、首先,收集 Compiled Resources 中被替换的预定义版本的资源名称
通过查看 Zip 格式资源包中每个 ZipEntry 的 CRC-32 checksum 来寻找被替换的预定义资源,预定义资源的 CRC-32 定义在 ResourceUsageAnalyze 中,如下所示:
// A 1x1 pixel PNG of type BufferedImage.TYPE_BYTE_GRAY
public static final long TINY_PNG_CRC = 0x88b2a3b0L;
// A 3x3 pixel PNG of type BufferedImage.TYPE_INT_ARGB with 9-patch markers
public static final long TINY_9PNG_CRC = 0x1148f987L;
// The XML document as binary-packed with AAPT
public static final long TINY_XML_CRC = 0xd7e65643L;
2)、然后,使用 android-chunk-utils 把 resources.arsc 中对应的定义移除。
3)、最后,删除资源包中对应的资源文件即可。
2、重复资源优化
在大型 App项目的开发中,一个App一般会有多个业务团队进行开发,其中每个业务团队在资源提交时的资源名称可能会有重复的,这将会引发资源覆盖的问题,因此,每个业务团队都会为自己的资源文件名添加前缀。这样就导致了这些资源文件虽然内容相同,但因为名称的不同而不能被覆盖,最终都会被集成到APK包中。这里,我们还是可以在 Android 构建工具执行 package${flavorName}Task 之前通过修改 Compiled Resources 来实现重复资源的去除,具体放入实现原理可细分为如下三个步骤:
- 1)、首先,通过资源包中的每个ZipEntry的CRC-32 checksum来筛选出重复的资源。
- 2)、然后,通过android-chunk-utils修改resources.arsc,把这些重复的资源都重定向到同一个文件上。
- 3)、最后,把其它重复的资源文件从资源包中删除,仅保留第一份资源。
具体的实现代码如下所示:
variantData.outputs.each {
def apFile = it.packageAndroidArtifactTask.getResourceFile;
it.packageAndroidArtifactTask.doFirst {
def arscFile = new File(apFile.parentFile, "resources.arsc");
JarUtil.extractZipEntry(apFile, "resources.arsc