APK内容及编译过程分析

目录

APK内容及编译过程分析

1. 概述

2. APK解压后文件说明

3. APK编译过程分析

 

  1. 概述

APK全称Android application package,是一种Android应用程序包。

一个Android应用程序的代码想要在Android设备上运行,必须先进行编译,然后被打包成为一个被Android系统所能识别的文件才可以被运行,而这种能被Android系统识别并运行的文件格式便是“APK”。 一个APK文件内包含被编译的代码文件(.dex 文件),文件资源(resources), 原生资源文件(assets),证书(certificates),清单文件(manifest file)。

    2. APK解压后文件说明

apk实际上是一种压缩包,将.apk文件修改后缀为.zip文件,解压后可以看到以下目录:

android 设置apk编译路径设置 apk编译流程_Android

 

    2.1 assets文件夹:

assets文件夹主要存放原生资源文件,即Android工程下的assets文件夹下的内容合集。

    2.2 lib文件夹:

lib文件夹主要存放编译后的库文件。

    2.3 META-INF文件夹:

META-INF文件夹主要存放的是Android组件版本相关信息,以及编译生成的签名校验信息,主要作用是在apk安装时校验apk是否被修改过。

例如组件版本信息:

android 设置apk编译路径设置 apk编译流程_android_02

 

例如签名校验信息:

android 设置apk编译路径设置 apk编译流程_android 设置apk编译路径设置_03

 

MANIFEST.MF文件:

清单文件,记录了所有其他文件的SHA1并base64编码值。

 

CERT.SF文件:

分为两部分,头部的

SHA-256-Digest-Manifest: A9MrU52Yl1nxQtwWGN0GCnlKg0DFAbGsmH4H508Wt/8=为MANIFEST.MF文件的SHA1并base64编码值;后面的每一个子项,对应MANIFEST.MF子项的SHA1并base64编码值;

 

CERT.RSA文件:

包含了公钥信息和发布机构信息。

 

    2.4 res文件夹:

即Android工程的res文件合集,在aapt编译期,所有res由gradle的task进行merge。改文件内的每一个文件都对应R.java下的一个静态常量。

 

    2.5 .dex文件

上图上包含三个.dex文件,由java编译器生成的class文件经过dx工具转换而来。

DEX文件从整体上来看是一个索引的结构,类名、方法名、字段名等信息都存储在常量池中,这样能够充分减少存储空间,相较于Java字节码文件更适合手机设备。

逻辑上,可以把dex文件分成3个区,头文件、索引区和数据区。索引区的ids后缀为identifiers的缩写。

android 设置apk编译路径设置 apk编译流程_android_04

 

 

    2.6 resource.arsc文件:

编译过程中产生的一个资源索引文件。

 资源索引具有固定的格式:0xPPTTEEEE = PackageId(2位) + TypeId(2位) + EntryId(4位)。

PP:Package ID,包的命名空间,取值范围为[0x01, 0x7f],第三方应用均为7f。

TT:资源类型,有anim、layout、mipmap、string、style等资源类型。

EEEE:代表某一类资源在偏移数组中的值。

该文件的结构如下图示:

android 设置apk编译路径设置 apk编译流程_android 设置apk编译路径设置_05

 

    2.7 AndroidManifest.xml文件:

Android工程中AndroidManifest.xml的合集,主要记录Android组件声明,权限声明,应用名称声明等相关信息。

该文件直接打开会乱码,可使用AXMLPrinter2.jar逆向编译xml,命令如下:

java -jar AXMLPrinter2.jar AndroidManifest.xml ->AndroidManifest.txt

    3. APK编译过程分析

 

通常一个Android工程生成一个apk文件,需要经过以下步骤:

 

    3.1Aidl文件编译

通过 aidl 工具转换成编译器能够处理的 Java 接口文件。

 

    3.2 aapt(Android Asset Packaging Tool):

3.2.1 aapt/aapt2编译资源文件为二进制,将资源文件生成扩展名为.flat的二进制文件。

3.2.2 合并所有已编译的文件并打包到一个软件包中。

3.2.3 AndroidManifest.xml、布局文件等xml 资源被aapt/aapt2处理成resource.arsc文件。

    3.3 java compiler

javac工具编译java文件(R.java、Java 接口文件、Java 源文件)为class文件。

 

 

    3.4 编译class文件

因为 .class 并不是 Android 系统所能识别的格式,所以还需要通过 dex 工具将它们转化为相应的 Dalvik 字节码(包含压缩常量池以及清除冗余信息等工作)。这个过程中通常还会加入应用所依赖的所有第三方库。

这个过程中涉及到早期的dx工具以及Android Studio3.1之后引入的D8编译器和R8工具。

早期,Android从源码到字节码再到dex的编译过程如下,中间使用Proguard进行字节码优化:

android 设置apk编译路径设置 apk编译流程_编译过程_06

 

由于过程复杂,2015年左右Google退出了Jack&Jill编译器,减少了中间环节:

android 设置apk编译路径设置 apk编译流程_Android_07

 

过与简化的编译过程,无法在过程中加入更多优化,因此Google在2017年废弃了Jack&Jill,重新回到了之前的编译流程,只是重写了最后一步dex编译器,称之为D8。一个完整的编译流程需要Proguard和D8同时参与其中。

android 设置apk编译路径设置 apk编译流程_android_08

 

R8的出现整合了Proguard和DB,减少了一个编译步骤,同时保留了字节码优化的能力。

android 设置apk编译路径设置 apk编译流程_android_09

 

R8通过开启Shrinking,可以对源码进行优化,例如不可达到代码删除,代码优化,代码混淆。

android { ... buildTypes { release { minifyEnabled false } } }

示例:

android 设置apk编译路径设置 apk编译流程_编译过程_10

 

 

    3.5 生成APK包(apkbuilder/zipflinger

将manifest文件、resources文件、dex文件、assets文件等等打包成一个压缩包,也就是apk文件。

 

    3.6 签名(apksignerr/jarsigner)

v1签名:

v1签名方式主要是利用META-INFO文件夹中的三个文件。

首先,将apk中除了META-INFO文件夹中的所有文件进行进行摘要写到 META-INFO/MANIFEST.MF;然后计算MANIFEST.MF文件的摘要写到CERT.SF;最后计算CERT.SF的摘要,使用私钥计算签名,将签名和开发者证书写到CERT.RSA。

所以META-INFO文件夹中这三个文件就能保证apk不会被修改。
但是缺点也很明显,META-INFO文件夹不会被签名,所以美团针对这种签名方式设计了一种多渠道打包方案:

利用pythone在META-INFO文件夹中创建一个文件,其名称就是渠道名,然后用java去读取文件名获取渠道。

 

v2签名:

Android7.0之后,推出了v2签名,为了解决v1签名速度慢以及签名不完整的问题。

apk本质上是一个压缩包,而压缩包文件格式一般分为三块:

文件数据区,中央目录结果,中央目录结束节。

而v2要做的就是,在文件中插入一个APK签名分块,位于中央目录部分之前,如下图:

android 设置apk编译路径设置 apk编译流程_android_11

 

V3签名

Android 9 推出了v3签名方案,和v2签名方式基本相同,不同的是在v3签名分块中添加了有关受支持的sdk版本和新旧签名信息,可以用作签名替换升级。

 

V4签名

Android 11 推出了v4签名方案。

v4 签名基于根据 APK 的所有字节计算得出的 Merkle 哈希树。它完全遵循 fs-verity 哈希树的结构,将签名存储在单独的.apk.idsig 文件中。

    3.7 ZipAlign对齐

对齐的过程就是将 APK 文件中所有的资源文件距离文件的起始位置都偏移4字节的整数倍,这样通过 mmap 访问 APK 文件的速度会更快,并且会减少其在设备上运行时的内存占用。

zipalign 是一种归档对齐工具,可对 Android 应用 (APK) 文件提供重要的优化
它会使 APK 中的所有未压缩数据(例如图片或原始文件)在 4 字节边界上对齐。
这里涉及到一个Data structurealignment(数据对齐)的知识点,其大概意思就是如果数据是自然对齐的,CPU读写就会更高效。

签名工具的不同带来的对齐处理的顺序不同:

如果使用的是 apksigner,只能在为 APK 文件签名之前执行 zipalign。
如果使用的是 jarsigner,只能在为 APK 文件签名之后执行 zipalign。