An APK file contains all of a program’s code (such as .dex file), resources, assets, certificates, and manifest file.
APK文件本身是个压缩文件,我们可以通过一个简单的 Hello Android 示例和一个复杂的应用,对比其对应的APK文件。 首先新建一个简单的 Hello Android 项目,build出一个APK文件后拖到AS中利用自带的 apkanalyzer 打开,基本组成如图:
再打开一个下载好的比较复杂的APK(微信), 如图
无论是简单的Hello world 还是复杂的WeChat,可以看出其主要组成包括:
那这些文件到底是怎么生成的,接下来看看一个APK文件的构建流程。
Android Studio默认采用gradle组织完成打包过程,对开发者来说简单的执行相关的task即可,这种透明的打包过程也让我们忽略了很多细节,这里我们以上述APK文件结构作为对照,来一窥源码结构变成最终的APK文件的过程。
以 Hello Android 为例,对比源码中的AndroidManifest.xml 与 APK中的AndroidManifest.xml 可以明显看出
会新增一些之类的标签,之前的theme、label等字符串被替换为具体的引用。这一过程发生在资源打包过程中,对这一过程不熟悉的可参考我之前的 资源打包流程, 简单说就是gradle在打包过程中会通过命令行启动 aapt 工具, aapt在打包资源过程中会进行Merge Manifest,对AndroidManifest.xml文件进行修改。 注意这里是通过AndroidStudio自带的分析器打开APK才能明确看到AndroidManifest.xml中的内容,如果只是简单的解压APK后AndroidManifest.xml被编译成了二进制文件。
对比源码和打包后res目录的变化,也会发现res中的drawable中的图片等资源没有发生明显变化(其实会部分压缩),layout/下的xml文件也发生了类似AndroidManifest.xml的字符串替换和编译,变成了二进制文件,而values/不见了。对res/的处理也发生在aapt编译资源的过程中,对其中的xml文件进行编译和字符串裁剪,部分xml资源会被编译进resources.arsc。
resources.arsc 也是个二进制文件,是aapt打包资源过程中的产物,同时伴随着上述资源文件的编译和压缩,其中res/value其实就编译进了resources.arsc, 其他的资源文件的id和路径对应关系也写进了这个文件,便于运行时访问。
对资源打包和访问不太熟悉的可以参考:
Android 资源框架:资源打包流程
Android 资源框架:资源的运行时访问
目录中的文件未发生变化
无变化
dex文件是Dalvik 和 ART 可执行格式。相较于JVM,dex将多个class文件打包到了一起,因此从Java文件到dex,需要经历java--->class-->dex的过程;
相较于class文件,dx工具在生成dex文件的过程中,会对字符常量池和一些冗余信息会进行压缩,结构上比class文件也更加紧凑。
通过 apkbuilder 工具,将aapt处理后的相关资源文件和利用dx处理生成的Classes.dex打包生成APK文件, apkbuilder 工具也位于 sdk/build-tools/
借助 zipalign 工具,对APK文件进行对齐处理:
zipalign 是一种归档对齐工具,可对 Android 应用文件进行重要的优化。其目的是要确保所有未压缩数据的开头均相对于文件开头部分执行特定的对齐。具体来说,它会使 APK 中的所有未压缩数据(例如图片或原始文件)在 4 字节边界上对齐。这样一来,即可使用 mmap() 直接访问所有部分,即使其中包含具有对齐限制的二进制数据也没关系。这样做的好处是可以减少运行应用时消耗的 RAM 容量。
通过 apksigner 工具,对生成的APK文件进行签名;
以上用到的相关脚本及工具均位于 sdk/build-tools/
具体的细节可参考其对应source code