不管你是做移动开发、前端开发还是后端开发,一定会遇到一个概念——打包,那打包是什么?为什么要打包?以及如何打包呢?
打包是什么,为什么要打包
在我们日常生活中有哪些常见的打包场景呢?旅行打包、快递打包、礼品打包、资料文档打包,这些打包都是指把若干物品在空间上有机的组合在一起,对外呈现为一个整体的过程。 打包的目的是为了更方便、安全地储存、运输、呈现和使用物品。
在技术领域依旧如此,打包一般指将程序的源代码、资源文件和依赖库等整合成一个整体的过程,打包是为了程序能够更加方便、安全的使用(包括存储、传输、复制、部署、安装、运行等)。
如何打包
马上过年了,X厂要发放新年大礼包给员工,那制作过程大概是这样的:确定大礼包物品清单以及包装盒样式→确定每个物品摆放位置和顺序→按顺序对物品进行摆放→封包→打上防伪标签,在这个打包过程中,其实还隐藏着另一个流程,那就是每个步骤所需要的环境和工具的准备。在技术领域,打包过程亦是如此,接下来就Android打包过程具体展开:
Android 打包流程
打包在Android上也叫APK构建,打包就是把Android工程源码变为APK的过程,使用的工具叫Gradle。首先我们得有个认知,每个APK构建的大流程是确定的,只是不同的APP依赖三方库(物品清单一部分)、签名(防伪手段)、使用工具集、打包后存放位置一些自定的地方不同,我们才需要进行对打包控制文件(build.gradle)进行配置。我们觉得打包流程不容易理解是因为我们能够操作的build.gradle等文件都只是配置文件,打包的内部逻辑和流程是对我们隐藏的。打包跟其他任何工作一样都是分为三步走:
确定工作内容和范围→确定工作分工和顺序→顺序执行每个任务,分别对应上述流程,Android打包也分为三个大的步骤:
- 初始化(Initialization)
- 配置(Configuration)
- 执行(Execution)
我们在setting和build.gradle的配置也是在初始化和配置阶段生效的。下面我们逐个进行介绍
一 、初始化 Initialization
- 从工程源码根目录开始查找setting.gradle文件;
- 根据setting.gradle文件中的配置来决定哪些模块参与构建;
- 首先为根build.gradle生成一个Project实例,接下来为每一个setting.gradle中配置的模块(对应build.gradle)生成一个Project实例,根build.gradle对应的Project是其他build.gradle对应Project的父Project;
注意:
① 因为每个build.gradle 都对应一个Project对象,Project对象类似于Android中的 Context,在build.gradle中获取gradle系统方法都是通过内置的project对象或者 getProject() 得到上下文后去获取。
② 这个阶段我们配置的task、回调都不会被执行,可以认为是Android中的类加载到内存和实例对象初始化阶段。
二、配置 Configuration
1. 对每个build.gradle对应的Project进行配置,所谓的配置主要是指 ① 下载并初始化插件 ②配置 Android 的编译版本、签名、产品风味等 ③ 下载依赖库 ④注册自定义Task 等,其实这四个主要事情也是一个build.gradle中所有的内容。project配置和执行都是采用广度优先遍历,那就是父project先执行,然后是按照Setting.gradle中顺序执行子project, 这个阶段也叫 Project Evaluation
2. 执行Project evaluation的回调,比如Project.beforeEvalution 、Project.afterEvalution等;
3. 广度优先顺序为每个Project注册task,执行task create回调(如果有),示例如下:
tasks.whenTaskAdded { task ->
println("hahaha task added call back name= "+task.name)
}
4. 为每一个Task创建一个依赖图,依赖图就是来说明运行这个task要先执行哪些Task的图;
5. 当所有Task的依赖图构建完毕,发送 task构建图完成通知。如果用户定义了回调就可以收到
project.gradle.taskGraph.addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
@Override
void graphPopulated(TaskExecutionGraph graph) {
println("hahaha task TaskExecutionGraph call back")
}
})
小结:配置就是决定打包的具体内容、使用工具和详细流程。我们大部分在build.gradle中自定义的东西都是在配置阶段生效和执行。
注意:
- 我们在Android studio执行sync的时候其实就是执行了初始化+配置,我们配置了更多的东西比如更多三方库,更多构建变体 这个sync时间就会更长;
- 执行任何Task都要重新执行整个项目的初始化和配置阶段,但是如果之前执行过sync或者这两个步骤,而且所有的buid.gradle都未变化,gradle内部会找到之前的缓存并跳过一个个任务的执行。
三、执行:Execution
其实前面两个阶段都是读入决策和准备阶段,真正做事都是在这个阶段——打包(也叫构建、assemble),根据不同的构建类型和产品风味会有多个打包产物,接下来我们就指对最常见的assembleRelease进行展开。
本文前面讲所谓打包就是把Android App源码变为APK的过程,Android App工程源码是什么,结构是怎么样的,做Android开发的应该都比较清晰,主要有AndroidManifest配置文件、java代码、kotlin代码、XML、图片、依赖库地址、本地库(jar、aar、so)等,这里就展开介绍;而APK其实就是一个包含编译后的代码和资源的压缩文件,我们找到一个APK(改后缀为.zip)解压得到文件列表如下:
重点模块介绍如下:
1、META-INF
META是Meta Data的缩写,元数据是描述数据的数据,APK是一个压缩包,可以认为是一种数据结构,那它的Meta-data就是描述这个APK相关信息的数据,比如使用三方库版本号、JAR包签名(V1签名文件,V2签名等会是签名在包装APK之上,打开后就破坏了这个签名)等。
2、res
res目录下包含了整个APP工程的资源,就是说打包时候会把一个APP所有模块,依赖库中的资源挑选出来,进行合并 、缩减(比如开启R8优化,工程种没有使用的资源就不会打包到APK),重命名等操作,最后整理出来一份资源放这里,它对应着我们工程目录中的资源目录:
3、AndroidManifest.xml
AndroidManifest.xml包含了整个APP所有模块的配置文件,就是说打包时候会把一个APP所有模块、依赖库中的AndroidManifest文件拿出来合并、优化得到这一份总的AndroidManifest配置文件。
4、classes.dex
classes.dex 文件是 对应Android 工程中的Java和Kotlin源码,是他们经过编译后生成的 Android虚拟机(Dalvik、ART) Executable(可执行) 文件,.dex 后缀就是 Dalvik Excecutable的缩写。
5、resources.arsc
resources.arsc是所有模块的资源地址映射表合集, 就是说我们在代码中寻找资源是 R.id.XX,但是实际读一个文件是需要文件路径的,这个文件就是把Id映射为路径的表。
6、assets
assets目录就是我们原来放在各个模块assets目录下的so、图片、apk等文件的合集,这部分是直接移动复制过来,没有优化、没有压缩,所以搞包体积优化这地方是重点。
7、lib目录
lib目录存储了整个工程的所有so文件,这些so文件来源于本模块、依赖模块、依赖库等地方,如果能够确定每个渠道包对应的设备使用CPU架构,可以在Android默配置、flavor 中配置, 配置后就只打包需要的SO文件,使用这个配置能有效减少APK包体积大小如下图:
接下来就谈谈这些产物是如何生成的,Android官方给出的App打包详细流转图如下:
注意:
- 这些流程是配置阶段决定好的,执行阶段就是按找之前决定的进行执行;
- 只要没有先后顺序的任务都是可以并行执行的,Gradle也是多线程工作的;
名词解释
AAPT
aapt 是Android Asset Packaging Tool的缩写,是编译和打包资源的工具,在SDK的build-tools目录下。
ApkBuilder
ApkBuilder 相当于一个压缩工具,它可以将已经编译好的 Java 代码、资源文件、AndroidManifest.xml等打包成一个 ZIP 文件,即 APK 文件
参考文档
https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:build_phases
https://blog.csdn.net/qq_38056514/article/details/127292335
https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
https://juejin.cn/post/7113713363900694565
http://tools.android.com/tech-docs/new-build-system/build-workflow