APK瘦身 - 基础

当我询问大多数开发者它们应用的大小时,我相当确信绝大部分人都会参照Android Studio生成的APK文件,然后告诉我这个APK文件在他们电脑上的物理大小。这是最简单的回答,并且也是正确的。但我可能要更进一步的询问了,请考虑下面的问题:

如果你是一个使用高端手机并且使用吃到饱的网络套餐的开发者,你可能怀疑将时间花费在优化APK文件大小上是否值得。但请记住:并不是所有的设备都有硬盘空间、内存空间和可靠的网络状态。

世界上总会有一些地方的人们必须为它们下载的每一个字节付费,并且Wi-Fi热点也并不是在每个角落都普及的。

有些设备没有足够的存储容量(硬盘空间)来让用户安装他们所需的所有应用,因此他们在安装或者更新任何软件的时候都会再三思考。也许这就是你的应用下一个数千或数百万用户,所以使者让你的APK更小!这对任何人都是有好处的。

当然,考虑到我们需要克服多种多样的需求和限制,因此很难去选择一个通用的解决方案。有时,牺牲首次下载的大小将加快后续的更新。但在另外一方面,和直接恰恰相反,保持APK中的文件为未压缩的状态可以减少最终占用设备的硬盘空间大小。我将试着去调调这些差异并且在适当的地方给出解释。但最终的决定权在于你,去选择混合一些对你的应用和用户有意义的技术。

运行时的内存占用问题并不是本系列文章的主要内容。但优化应用大小最终可能对应用内存占用问题产生优缺点共存的影响。我将试着在合适的时间讲解这其中的缺点,但需要读者自己去测试你自己应用的性能产生的影响并对每种优化方法进行权衡。

APK 中都有什么

在我们讨论如何减少app体积之前,让我们首先来看一下文件本身的格式。一个APK文件仅仅是一个zip包。包含了制作应用的文件。在APK 中,你通常会找到如下资源(译者注:使用zip工具打开即可查看):

classes.dex

classes.dex包含了编译成为dex字节码的应用代码。如果你使用了multidex机制来修复65536个方法数限制的问题,那么你可能在你的APK文件中看到多个dex文件。从Android 5.0开始引进了ART运行时的概念,这些dex文件将在应用安装时提前被编译成OAT文件,并将OAT文件放在设备的data分区。你可以在第二部分学习到如何减少dex字节码的大小。

res/

这个文件夹中包含了大部分存在于各种限定符文件夹中的XML资源(如 layout)、图片资源(png、jpeg),例如-mdpi,-hdpi用来声明屏幕密度, -sw600dp ,-large用来声明屏幕的尺寸,-en-de-pl用来声明语言。需要注意的是在res中的任何xml文件都在编译期间被转换成一种更紧凑的二进制文件,所以你不能在APK中通过文本编辑器来打开它们。

第三部分会介绍如何保证你的应用没有因为无用的资源来浪费空间。在第四部分和第五部分我们将会讨论如何根据其硬件特性来针对特殊拥有特殊设备的人群进行APK资源的分配。第六部分和第七部分将会通过多种优化技术来让图片更小。

resourses.arsc

一些资源和标识符将被编译并平铺在这个文件中。它们通常被存储在APK中,不会被压缩,以便在运行时可以更快速的进行访问。手动的压缩这些文件似乎更容易减少应用的体积,但实际上至少有两个原因来证明这并不是一个好主意。一是Play商店在传输时就对数据进行了压缩,而是将文件压缩在APK内部会浪费系统资源(RAM)和性能(尤其是应用启动的时间)。

第三部分将会通过只包含对你应用有意义的语言字符串来使得这个文件更精简。

AndroidManifest.xml

和其它的XML资源类似,你的应用程序的Manifest文件也将在编译期间被转换为二进制文件。Play商店通过读取AndroidManifest文件中的一些特定的信息来决定一个APK是否可以被安装在设备上。检查允许的屏幕密度、屏幕尺寸和可用的硬件以及其它特性(例如是否支持触摸)。如果你希望在被编译后查看这些条目,你可以使用Android SDK中的aapt工具:

aapt dump badging your_app.apk

libs/

任何native文件(*.so文件)都会以ABI(cpu架构,如x86、x86_64、armeabi-v7a等)命名放在libs子文件夹中。通常它们在应用安装时会被系统从APK中拷贝到/data分区。然而,当APK存在于用户设备上时,就不会再改变了,因此,这样拷贝的做法无疑会对任何native库都会增加双倍的占用空间。第八部分将提供一种解决办法适用于6.0+的系统。同时在比较旧的设备上用户也会节省网络带宽。

assets/

这个文件夹下存放的文件将不被当作Android类型资源。通常是字体文件或者游戏数据,例如等级数据和纹理信息,以及你希望可以直接通过文件流的形式打开的应用数据。

META-INF/

这个文件夹存在于被签名的APK中,包含了APK中所有的文件和签名列表。目前Android签名的机制是一个接一个地验证来自存档的未压缩文件内容的签名。

这会产生一些比较有趣的结果。因为每个一个zip包中的每个条目是被单独存储的,这意味着你可以修改一个单独条目文件的压缩级别而不需要重新签名。然而如果你在签名后从zip包中移除任何文件都将导致签名失败。

一个需要值得注意的点是一个签名的APK通常需要通过使用zipalign工具作为最后构建的步骤来进行创建。如果你手动更改了zip包中的内容,通常你需要重新签名,然后经过zipalign,最后上传APK至Play商店。

[^https://medium.com/google-developers/smallerapk-part-1-anatomy-of-an-apk-da83c25e7003]: