阅读 206

APK文件浅析-打包流程

本文从宏观视角来看看APK文件结构及其设计思想,借助一个简单的“Hello Android”为例说明

APK文件结构

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打开,基本组成如图:

Hello Android
再打开一个下载好的比较复杂的APK(微信), 如图
WeChat
无论是简单的Hello world 还是复杂的WeChat,可以看出其主要组成包括:

  1. AndroidManifest.xml :用于声明应用包名信息、相关的使用权限、四大组件、android sdk版本等;
  2. res目录:包含程序主要资源文件,包括res/layout、res/drawable等,主要是布局文件和图片等资源。(微信是对资源做了混淆,字符串裁剪,上图中的r=res);
  3. resources.ars:资源映射表,包含一部分编译资源(res/value)和资源路径, 可以通过资源R.java中的ID映射到对应的资源文件或者值;
  4. assets目录:包含不被压缩的原始文件,主要是一些多媒体文件,或者附加文件;
  5. lib目录:使用到的各种.so链接库;
  6. Classes.dex:打包好的程序代码,如果是复杂的应用会进行分包,包含多个dex文件(微信);
  7. META-INF目录:主要包含APK的签名信息
    • MANIFEST.MF:清单文件,当前APK中所有文件清单及其对应hash值;
    • CERT.SF:上述清单文件中的每条信息的hash值;
    • CERT.RSA:对CERT.SF文件的数字签名以及签名时所用的数字证书;

那这些文件到底是怎么生成的,接下来看看一个APK文件的构建流程。

APK打包流程

Android Studio默认采用gradle组织完成打包过程,对开发者来说简单的执行相关的task即可,这种透明的打包过程也让我们忽略了很多细节,这里我们以上述APK文件结构作为对照,来一窥源码结构变成最终的APK文件的过程。

1. AndroidManifest.xml

Hello Android为例,对比源码中的AndroidManifest.xml 与 APK中的AndroidManifest.xml 可以明显看出

AndroidManifest
会新增一些之类的标签,之前的theme、label等字符串被替换为具体的引用。这一过程发生在资源打包过程中,对这一过程不熟悉的可参考我之前的 资源打包流程, 简单说就是gradle在打包过程中会通过命令行启动 aapt工具,aapt在打包资源过程中会进行Merge Manifest,对AndroidManifest.xml文件进行修改。 注意这里是通过AndroidStudio自带的分析器打开APK才能明确看到AndroidManifest.xml中的内容,如果只是简单的解压APK后AndroidManifest.xml被编译成了二进制文件。

2. res/

对比源码和打包后res目录的变化,也会发现res中的drawable中的图片等资源没有发生明显变化(其实会部分压缩),layout/下的xml文件也发生了类似AndroidManifest.xml的字符串替换和编译,变成了二进制文件,而values/不见了。对res/的处理也发生在aapt编译资源的过程中,对其中的xml文件进行编译和字符串裁剪,部分xml资源会被编译进resources.arsc。

3. resources.arsc

resources.arsc 也是个二进制文件,是aapt打包资源过程中的产物,同时伴随着上述资源文件的编译和压缩,其中res/value其实就编译进了resources.arsc, 其他的资源文件的id和路径对应关系也写进了这个文件,便于运行时访问。

对资源打包和访问不太熟悉的可以参考:

Android 资源框架:资源打包流程

Android 资源框架:资源的运行时访问

4. assets/

目录中的文件未发生变化

5. lib/

无变化

6. Classes.dex

dex文件是Dalvik 和 ART 可执行格式。相较于JVM,dex将多个class文件打包到了一起,因此从Java文件到dex,需要经历java--->class-->dex的过程;

  • 如果用到了AIDL,通过sdk/build-tools/xxx/aidl 工具解析aidl文件,生成对应的Java文件;
  • Compile所有的 Java文件生成 .class文件;
  • 通过dx脚本,将所有class文件包括第三方Jar包等处理为dex文件,如果配置了multidex, 可能会生成多个classes.dex文件。dx及其他相关脚本位于sdk/build-tools/xxx/

相较于class文件,dx工具在生成dex文件的过程中,会对字符常量池和一些冗余信息会进行压缩,结构上比class文件也更加紧凑。

7. 打包

通过apkbuilder工具,将aapt处理后的相关资源文件和利用dx处理生成的Classes.dex打包生成APK文件,apkbuilder工具也位于 sdk/build-tools/

8. 对APK进行对齐处理

借助zipalign工具,对APK文件进行对齐处理:

zipalign 是一种归档对齐工具,可对 Android 应用文件进行重要的优化。其目的是要确保所有未压缩数据的开头均相对于文件开头部分执行特定的对齐。具体来说,它会使 APK 中的所有未压缩数据(例如图片或原始文件)在 4 字节边界上对齐。这样一来,即可使用 mmap() 直接访问所有部分,即使其中包含具有对齐限制的二进制数据也没关系。这样做的好处是可以减少运行应用时消耗的 RAM 容量。

9.签名

通过apksigner工具,对生成的APK文件进行签名;

以上,我们就得到了一个包含 代码 + 资源 + 签名信息的可独立安装运行的APK文件。

以上用到的相关脚本及工具均位于 sdk/build-tools/

sdk build-tools
具体的细节可参考其对应source code

总结

我们首先分析了一个基本的APK文件的组成部分,之后拆解了每一个组成文件的生成过程及其相关脚本。如果你对aaptdxapksigner的具体工作流程感兴趣,可以查找并阅读其源码。再来看官方打包流程图,相信你会更加清晰!

apk build

相关阅读:

Android MultiDex 分包及加载原理

Android 资源框架:资源打包流程

Android 资源框架:资源的运行时访问

关注下面的标签,发现更多相似文章
评论