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:
gradlew assemble
可以由此入手,看下打包一次都涉及到了哪些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的过程。