Android 完整的apk打包流程
在Android Studio中,我们需要打一个apk包,可以在Gradle task 任务中选一个
assembleDebug/assembleRelease 任务,
控制台上就可以看到所有的构建相关task:
可以看到,这么多个task任务,执行是有先后顺序的,其实主要就是以下步骤:
//aidl 转换aidl文件为java文件
> Task :app:compileDebugAidl
//生成BuildConfig文件
> Task :app:generateDebugBuildConfig
//获取gradle中配置的资源文件
> Task :app:generateDebugResValues
// merge资源文件
> Task :app:mergeDebugResources
// merge assets文件
> Task :app:mergeDebugAssets
> Task :app:compressDebugAssets
// merge所有的manifest文件
> Task :app:processDebugManifest
//AAPT 生成R文件
> Task :app:processDebugResources
//编译kotlin文件
> Task :app:compileDebugKotlin
//javac 编译java文件
> Task :app:compileDebugJavaWithJavac
//转换class文件为dex文件
> Task :app:dexBuilderDebug
//打包成apk并签名
> Task :app:packageDebug
依靠这些关键步骤最后就能打包出一个apk。
首先看
第一步:aidl(编译aidl文件)
将项目中的aidl
文件编译为java
文件,AIDL用于进程间通信
第二步:生成BuildConfig文件
在项目中配置了buildConfigField
等信息,会在BuildConfig class类里以静态属性的方式展示:
第三步:合并Resources、assets、manifest、so等资源文件
在我们的项目中会依赖不同的库、组件,也会有多渠道的需求,所以merge
这一步操作就是将不同地方的资源文件进行整合。
多个manifest文件也需要整理成一个完整的文件,所以如果有属性冲突这一步就会报错。资源文件也会整理分类到不同的分辨率目录中。
资源处理用的工具是aapt/aapt2
注意:AGP3.0.0
之后默认通过AAPT2
来编译资源,AAPT2
支持了增量更新,大大提升了效率。
AAPT 工具负责编译项目中的这些资源文件,所有资源文件会被编译处理,XML 文件(drawable 图片除外)会被编译成二进制文件,所以解压 apk 之后无法直接打开 XML 文件。但是 assets 和 raw 目录下的资源并不会被编译,会被原封不动的打包到 apk 压缩包中。
资源文件编译之后的产物包括两部分:resources.arsc 文件和一个 R.java。前者保存的是一个资源索引表,后者定义了各个资源 ID 常量。这两者结合就可以在代码中找到对应的资源引用。比如如下的 R.java 文件:
实际上被打包到 apk 中的还有一些其他资源,比如 AndroidManifest.xml 清单文件和三方库中使用的动态库 .so 文件。
第四步:编译java文件(用到的工具 javac )
1、java文件包含之前提到的AIDL 生成的java文件
2、java代码部份:通过Java Compiler
编译项目中所有的Java
代码,包括R.java
、.aidl
文件生成的.java
文件、Java
源文件,生成.class
文件。在对应的build
目录下可以找到相关的代码
3、kotlin代码部份:通过Kotlin Compiler
编译项目中的所有Kotlin
代码,生成.class文件
注:注解处理器(APT
,KAPT
)生成代码也是在这个阶段生成的。当注解的生命周期被设置为CLASS
的时候,就代表该注解会在编译class
文件的时候生效,并且生成java
源文件和Class
字节码文件。
第五步: Class
文件打包成DEX
(dx/r8/d8等工具编译class文件)
- 在原来
dx
是最早的转换工具,用于转换class文件为dex文件。 - Android Studio 3.1之后,引入了
D8
编译器和R8
工具。 - Android Studio 3.4之后,默认开启 R8
具体的区别可以点击看看
注意:JVM
和 Dalvik(ART)
的区别:JVM执行的是.class文件、Dalvik和ART执行的.dex文件。具体的区别可以点击看看
而在编译class文件过程也常用于编译插桩,比如ASM,通过直接操作字节码文件完成代码修改或生成。
第六步:apkbuilder/zipflinger(生成APK包)
这一步就是生成APK文件,将manifest文件、resources文件、dex文件、assets文件
等等打包成一个压缩包,也就是apk文件。
在老版本使用的工具是apkbuilder
,新版本用的是 zipflinger
。
而在AGP3.6.0
之后,使用zipflinger
作为默认打包工具来构建APK
,以提高构建速度。
第七步: zipalign(对齐处理)
对齐是Android apk 很重要的优化,它会使 APK 中的所有未压缩数据(例如图片或原始文件)在 4 字节边界上对齐。这使得CPU读写就会更高效。
也就是使用工具 zipalign 对 apk 中的未压缩资源(图片、视频等)进行对齐操作,让资源按照 4 字节的边界进行对齐。这种思想同 Java 对象内存布局中的对齐空间非常类似,主要是为了加快资源的访问速度。如果每个资源的开始位置都是上一个资源之后的 4n 字节,那么访问下一个资源就不用遍历,直接跳到 4n 字节处判断是不是一个新的资源即可。
第八步: apk 签名
没有签名的apk 无法安装,也无法发布到应用市场。
大家比较熟知的签名工具是JDK
提供的jarsigner
,而apksigner
是Google
专门为Android
提供的签名和签证工具。
其区别就在于jarsigner
只能进行v1
签名,而apksigner
可以进行v2
、v3
、v4
签名。
v1签名
v1签名方式主要是利用META-INFO
文件夹中的三个文件。
首先,将apk中除了META-INFO文件夹中的所有文件进行进行摘要写到 META-INFO/MANIFEST.MF;然后计算MANIFEST.MF文件的摘要写到CERT.SF;最后计算CERT.SF的摘要,使用私钥计算签名,将签名和开发者证书写到CERT.RSA。
所以META-INFO文件夹中这三个文件就能保证apk不会被修改。
v2签名
Android7.0
之后,推出了v2签名,为了解决v1签名速度慢以及签名不完整的问题。
apk本质上是一个压缩包,而压缩包文件格式一般分为三块:
文件数据区,中央目录结果,中央目录结束节。
而v2要做的就是,在文件中插入一个APK签名分块,位于中央目录部分之前,如下图:
这样处理之后,文件就完成无法修改了,这也是为什么 zipalign(对齐处理)
要在签名之前完成。
v3签名
Android 9
推出了v3签名方案,和v2签名方式基本相同,不同的是在v3签名分块中添加了有关受支持的sdk版本和新旧签名信息,可以用作签名替换升级。
v4签名
Android 11
推出了v4签名方案。
最后,apk得以完成打包
PMS 在安装过程中会检查 apk 中的签名证书的合法性,具体安装apk内容稍后介绍。
apk内容包含如下:
总体的打包流程图如下:
,,
来源:juejin.cn/post/7206998548343668796