Google I/O 2016 笔记:APK 瘦身的正确姿势

Google I/O 2016 笔记:APK 瘦身的正确姿势

业务逻辑不断累积、引入的第三方 SDK 不断增加以及图片资源不变变大,APK 瘦身已经是每个 Android 应用开发者不得不考虑的问题了(iOS 应用也有瘦身的问题,但相比之下 Android 应用的瘦身压力大得多)。坊间流传的各种瘦身方法有些切实可行,有的却流毒甚广。不妨结合这届 I/O 的两个相关 topic 来学习下 APK 瘦身的正确姿势。

想更详细了解的同学可以过一下这两个 topic 的 video:

Image compression for Android developers

Lean and fast: Putting your app on a diet



先来看下图片压缩有关的问题

VD 千般好,但无奈换用它的成本略高,此次先不讨论。


WebP, PNG , JPG 三者的选择可以借助下图来判断:

如果只需要考虑 Android 4.0+, WebP 是个不错的选择,同画质下体积更小(在生产环境中使用最好谨慎些,在第三方 Rom 上多测试下)。PNG--> WebP 的格式转换十分简单:

brew install webp

对于 PNG 和 JPG,在按上面的流程图选择正确的类型后还需要进行一个步骤:压缩,这也是很多开发者忽略的一点。

个人最喜欢的图片压缩工具是 ImageOptim,集成了很多好用图片压缩库:

ImageOptim by Kornel Lesiński and contributors is a GUI for 3rd party utilities:

值得一提的是,借助 Zopfli,它可以在不改变 PNG 图像质量的情况下使图片大小明显变小,看疗效:

代价是压缩速度比较慢,不过从效果看,等待是值回票价的。


ImageOptim 的默认设置已经能获得不错的效果。但如果需要进一步压缩(有损压缩),我们需要做出一个抉择:我们可以牺牲多少图片质量?

回答这个问题需要先回答另一个问题,如何量化两张图片在视觉上的差别(psychovisual similarity of two images)。Google 提供了一个工具进行这种量化:butteraugli . 有兴趣的同学可以尝试一下

最后需要注意的是在生成 APK 时,aapt 默认会进行一些图片压缩的操作,但效果并不是太好。鉴于我们已经自己进行了这一步,相应的将 aapt 的相关操作跳过:

如果不跳过,aapt 并不知道图片已经压缩过了,就会出现上图中图片经过 aapt 处理大小反而变大的情况。



resources.arsc 和 .so

resources.arsc 中存储的是资源 ID 和资源名称的对应关系。从下图中可以看到在正常情况下这个文件在 apk 中是非压缩性存储的。然后就有好多文章和工具(真的是好多…)出于 APK 瘦身的目的,压缩了这个文件。看起来没有问题,但这其实确实不是一个好主意……

resources.arsc 在 APK 安装之后仍需要被频繁的读取,如果将它进行压缩,在每次读取前系统都必须进行解压的操作,从性能上考量这显然是得不偿失的。所以,赶紧摒弃通过压缩 resources.arsc 来给 APK 瘦身的方法吧。

resources.arsc 正确瘦身方式是删除不必要的 string entry. 借助 android-arscblamer 可以检查出可以优化的部分 ( eg. Null Entries )


Android 6.0 之前,在安装 APK 时,.so 会按照一定的逻辑被从 APK 中复制出来,这一流程也引入了一个长年大坑,掉坑者无数。从下图中我们可以看到 APK 安装后占用空间的情况。可以看到 解压后的 .so 这部分空间其实是可以省去的。


Android 6.0+ 后 .so 在安装中终于不用再复制出来,从而减少 APK 安装后对空间的占用,也避免了安装时由于 .so 拷贝失败导致的各种诡异的错误。

Android Studio 中只要做如下声明,在 Android 6.0+ 中就可以避免 .so 的拷贝了。需要指出的是,默认情况下,声明后所有 .so 都会非压缩性存储在 APK 中。希望不会再有不靠谱的 APK 瘦身工具对它们再动手脚。

此外,从 Android Nougat ( API 24 ) 开始,系统不再以整个 DEX 为粒度进行 AOT 也在一定程度上减少了 APK 安装后的体积占用。

最后要提一下,上面说过 resources.arsc 和 .so ( 6.0+ ) 都应该保持非压缩存储。但这显然会导致原始 APK 大小 ( Raw APK Size) 的增长。这里 Goolge 还另外提及了两个纬度:下载大小 ( Download Size )安装后大小 ( Installed Size )。如果从 Google Play 下载,下载大小是要小于原始 APK 大小的。这是由于 Google Play 对 APK 进行了压缩传输,和增量更新 ( 通过 binary diff ) 。可惜,不是每个第三方市场都做了这两点,摊手。下载大小作为应用开发者无法控制,但原始 APK 大小和安装后大小则是我们必须要考虑的问题。

编辑于 2016-07-11

文章被以下专栏收录