0x1、网上流传的三张APK打包流程图
Android官网 有一张新的打包流程图(左),相比起旧的流程图(右)更抽象,隐藏了很多细节:

在 Android Studio Project Site 还找到了一张更详细的图:

如果只是满足于一个写基础业务的Android开发仔,了解下足矣,不过如果想有更深的造诣,还是建议往下学的。
本节就来了解下AGP的构建过程,以及简单了解下APK的打包过程~
Tips:Tasks那么多,不可能一个个去精读源码解析,不同插件版本还有差异,不如授之以渔,本文的目的就是让读者遇到问题时懂得如何追根溯源,找到对应的源码。
0x2、如何查看插件源码
研究对象是AGP的源码,所以要先搞一份源码,方法有下述几种:
1. 下载完整源码
如果磁盘空间比较充足,可以通过repo的方式,将Android Gradle Plugin的源码下载到本地(貌似30多G):
# 最新源码的只有3.4.0的
repo init -u https://android.googlesource.com/platform/manifest -b gradle_3.4.0
repo sync
2. 下载部分源码
当然不需要编译的话,可以直接下对应源码包,来到下述地址:build-system
点tgz下载:

然后用VS Code之类的代码查看工具查看即可~
3. 取巧(推荐)
在app层级的build.gradle添加下述依赖:
implementation 'com.android.tools.build:gradle:3.4.0'
build下,然后在左侧 External Libraries
即可找到源码:

0x3、阅读源码前的一些补充
阅读源码前建议温习下我前面写的三篇文章,另外补充点姿势:
Gradle Plugin 中的Task主要有三种:普通Task
、增量Task
、Transform
。
Task一般会继承 DefaultTask
或 IncrementalTask
,而 @TaskAction
注解的方法,就是此Task做的事。
继承 IncrementalTask
的类为增量Task,这个增量是相对于全量来说的,全量指的是:调用完clean后第一次编译过程,修改代码或资源后再次编译,就是增量编译。几个关键方法:
public abstract class IncrementalTask extends BaseTask {
// 是否需要增量,默认false
@Internal protected boolean isIncremental() { }
// 需要子类实现,全量时执行的任务
protected abstract void doFullTaskAction() throws Exception;
// 增量时执行的任务,默认什么都不执行,参数是增量时修改过的文件
protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws Exception{ }
@TaskAction
void taskAction(IncrementalTaskInputs inputs) throws Exception {
// 判断是否是增量,是执行doIncrementalTaskAction,否则执行doFullTaskAction
}
// 获取修改文件
private Map<File, FileStatus> getChangedInputs(IncrementalTaskInputs inputs) { }
}
至于 Transform
(变换),是Android官方提供给开发者,在**.class → .dex转换期间用来修改.class文件的一套API**,留意transform()
方法的实现就好。
0x4、执行gradle assemble的Task链
我们常常使用下面的命令来打包APK:
可以由此入手,看下打包一次都涉及到了哪些Task,键入下述命令(linux、mac使用./gradlew):
gradlew assemble --console=plain
输出结果及要点简述如下所示:
:app:preBuild UP-TO-DATE → 空task,锚点
:app:preDebugBuild → 空task,锚点
:app:compileDebugAidl NO-SOURCE → 处理AIDL
:app:checkDebugManifest → 检查Manifest是否存在
:app:compileDebugRenderscript NO-SOURCE → 处理renderscript
:app:generateDebugBuildConfig → 生成 BuildConfig.java
:app:mainApkListPersistenceDebug → 生成 app-list.gson
:app:generateDebugResValues → 生成resvalue,generated.xml
:app:generateDebugResources → 空task,锚点
:app:mergeDebugResources → 合并资源文件
:app:createDebugCompatibleScreenManifests → manifest文件中生成compatible-screens,指定屏幕适配
:app:processDebugManifest → 合并manifest.xml文件
:app:processDebugResources → aapt打包资源
:app:compileDebugKotlin → 编译Kotlin文件
:app:prepareLintJar UP-TO-DATE → 拷贝 lint jar包到指定位置
:app:generateDebugSources → 空task,锚点
:app:javaPreCompileDebug → 生成 annotationProcessors.json 文件
:app:compileDebugJavaWithJavac → 编译 java文件
:app:compileDebugNdk → 编译ndk
:app:compileDebugSources → 空task,锚点
:app:mergeDebugShaders → 合并 shader文件
:app:compileDebugShaders → 编译 shaders
:app:generateDebugAssets → 空task,锚点
:app:mergeDebugAssets → 合并 assests文件
:app:validateSigningDebug → 验证签名
:app:signingConfigWriterDebug → 编写SigningConfig信息
:app:checkDebugDuplicateClasses → 检查重复class
:app:transformClassesWithDexBuilderForDebug → class打包成dex
:app:transformDexArchiveWithExternalLibsDexMergerForDebug → 打包第三方库的dex
:app:transformDexArchiveWithDexMergerForDebug → 打包最终的dex
:app:mergeDebugJniLibFolders → 合并jni lib 文件
:app:transformNativeLibsWithMergeJniLibsForDebug → 合并jnilibs
:app:transformNativeLibsWithStripDebugSymbolForDebug → 去掉native lib里的debug符号
:app:processDebugJavaRes NO-SOURCE → 处理java res
:app:transformResourcesWithMergeJavaResForDebug → 合并java res
:app:packageDebug → 打包apk
:app:assembleDebug → 空task,锚点
:app:extractProguardFiles → 生成混淆文件
# 还会打一个release包,task和上述基本一致,此处省略~
当然,也可以直接在 Build
窗口直接查看,双击右侧Gradle窗口中assemble的Task,然后观察此窗口:

啧啧,还可以看到每个Task的执行时间,不错,但先不跟每个Task具体内容,而是跟下AGP的构建过程~
0x5、AGP的构建过程
上一节将Gradle插件时说过,每个插件都会配置一个 id名字.properties
的文件,在此写上插件的实现类,全局搜定位到下述文件:

打开:

指向:AppPlugin
类,跟下:

上节说过:插件类都继承于 Plugin
,入口函数 apply()
,但在这里没找到,跟下:AbstractAppPlugin
→ BasePlugin
。
① BasePlugin

行吧,在BasePlugin中重写了 apply()
方法,里面调用了两个函数,先跟下:basePluginApply()

执行一些检查操作,接着是 插件的初始化及配置,而另一个函数:
pluginSpecificApply()
则是空实现,接着跟下:配置项目、配置扩展及创建Tasks的过程。