[关闭]
@coder-pig 2021-05-17T09:26:10.000000Z 字数 7858 阅读 488

补齐Android技能树——从AGP构建过程到APK的打包过程

Gradle


Android Gradle Plugin,简称 AGP,老早之前就想好好研究下Android APK的打包过程,毕竟作为APK包体积优化 的前置知识之一。奈何当时的知识储备严重不足,硬啃着实难受,在学了两周的Gradle后 ,我觉得应该有 一战之力 了,所以这一节来了!

内容较多,建议先收藏,有时间再慢慢细品~

0x1、网上流传的三种APK打包流程图

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

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

本节借鉴这三个图 + 插件源码,来了解AGP的构建过程及APK的打包过程~


0x2、如何查看插件源码

研究对象是AGP的源码,所以要先搞一份源码,方法有下述几种:

1. 下载完整源码

如果磁盘空间比较充足,可以通过repo的方式,将Android Gradle Plugin的源码下载到本地(貌似30多G):

  1. # 最新源码的只有3.4.0的
  2. repo init -u https://android.googlesource.com/platform/manifest -b gradle_3.4.0
  3. repo sync

2. 下载部分源码

当然不需要编译的话,可以直接下对应源码包,来到下述地址:build-system

点tgz下载:

然后用VS Code之类的代码查看工具查看即可~

3. 取巧(推荐)

在app层级的build.gradle添加下述依赖:

  1. implementation 'com.android.tools.build:gradle:3.4.0'

build下,然后在左侧 External Libraries 即可找到源码:


0x3、阅读源码前的一些补充

阅读源码前建议温习下我前面写的三篇文章,另外补充点姿势:

Gradle Plugin 中的Task主要有三种:普通Task增量TaskTransform

Task一般会继承 DefaultTaskIncrementalTask,而 @TaskAction 注解的方法,就是此Task做的事。

继承 IncrementalTask 的类为增量Task,这个增量是相对于全量来说的,全量指的是:调用完clean后第一次编译过程,修改代码或资源后再次编译,就是增量编译。几个关键方法:

  1. public abstract class IncrementalTask extends BaseTask {
  2. // 是否需要增量,默认false
  3. @Internal protected boolean isIncremental() { }
  4. // 需要子类实现,全量时执行的任务
  5. protected abstract void doFullTaskAction() throws Exception;
  6. // 增量时执行的任务,默认什么都不执行,参数是增量时修改过的文件
  7. protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws Exception{ }
  8. @TaskAction
  9. void taskAction(IncrementalTaskInputs inputs) throws Exception {
  10. // 判断是否是增量,是执行doIncrementalTaskAction,否则执行doFullTaskAction
  11. // 获取修改文件
  12. private Map<File, FileStatus> getChangedInputs(IncrementalTaskInputs inputs) { }
  13. }

至于 Transform(变换),是Android官方提供给开发者,在.class → .dex转换期间用来修改.class文件的一套API,留意
transform() 方法的实现就好。


0x4、执行gradle assemble的Task链

我们常常使用下面的命令来打包APK:

  1. gradlew assemble

可以由此入手,看下打包一次都涉及到了哪些Task,键入下述命令(linux、mac使用./gradlew):

  1. gradlew assemble --console=plain

输出结果及要点描述如下所示:

  1. :app:preBuild UP-TO-DATE task,锚点
  2. :app:preDebugBuild task,锚点
  3. :app:compileDebugAidl NO-SOURCE 处理AIDL
  4. :app:checkDebugManifest 检查Manifest是否存在
  5. :app:compileDebugRenderscript NO-SOURCE 处理renderscript
  6. :app:generateDebugBuildConfig 生成 BuildConfig.java
  7. :app:mainApkListPersistenceDebug 生成 app-list.gson
  8. :app:generateDebugResValues 生成resvaluegenerated.xml
  9. :app:generateDebugResources task,锚点
  10. :app:mergeDebugResources 合并资源文件
  11. :app:createDebugCompatibleScreenManifests manifest文件中生成compatible-screens,指定屏幕适配
  12. :app:processDebugManifest 合并manifest.xml文件
  13. :app:splitsDiscoveryTaskDebug 生成 split-list.json,用于json分包
  14. :app:processDebugResources aapt打包资源
  15. :app:compileDebugKotlin 编译Kotlin文件
  16. :app:prepareLintJar UP-TO-DATE 拷贝 lint jar包到指定位置
  17. :app:generateDebugSources task,锚点
  18. :app:javaPreCompileDebug 生成 annotationProcessors.json 文件
  19. :app:compileDebugJavaWithJavac 编译 java文件
  20. :app:compileDebugNdk 编译ndk
  21. :app:compileDebugSources task,锚点
  22. :app:mergeDebugShaders 合并 shader文件
  23. :app:compileDebugShaders 编译 shaders
  24. :app:generateDebugAssets task,锚点
  25. :app:mergeDebugAssets 合并 assests文件
  26. :app:validateSigningDebug 验证签名
  27. :app:signingConfigWriterDebug 编写SigningConfig信息
  28. :app:checkDebugDuplicateClasses 检查重复class
  29. :app:transformClassesWithDexBuilderForDebug class打包成dex
  30. :app:transformDexArchiveWithExternalLibsDexMergerForDebug 打包第三方库的dex
  31. :app:transformDexArchiveWithDexMergerForDebug 打包最终的dex
  32. :app:mergeDebugJniLibFolders 合并jni lib 文件
  33. :app:transformNativeLibsWithMergeJniLibsForDebug 合并jnilibs
  34. :app:transformNativeLibsWithStripDebugSymbolForDebug 去掉native lib里的debug符号
  35. :app:processDebugJavaRes NO-SOURCE 处理java res
  36. :app:transformResourcesWithMergeJavaResForDebug 合并java res
  37. :app:packageDebug 打包apk
  38. :app:assembleDebug task,锚点
  39. :app:extractProguardFiles 生成混淆文件
  40. # 还会打一个release包,task和上述基本一致,此处省略~

当然,也可以直接在 Build 窗口直接查看,双击右侧Gradle窗口中assemble的Task,然后观察此窗口:

啧啧,还可以看到每个Task的执行时间,不错,但先不跟每个Task具体内容,而是跟下AGP的构建过程~

0x5、AGP的构建过程

上一节将Gradle插件时说过,每个插件都会配置一个 id名字.properties 的文件,在此写上插件的实现类,全局搜定位到下述文件:

打开:

指向:AppPlugin 类,跟下:

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

① BasePlugin

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

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

② configureProject() → 配置项目

创建DataBindingBuilder实例,强制使用不低于当前所支持的最小插件版本,应用Java插件,如果启用了构建缓存选项,创建buildCache实例,添加了一个回调:所有project执行完后执行资源回收相关操作。

③ configureExtension() → 配置DSL扩展

完成下述几项工作:

④ createTasks() → 创建Tasks

跟下 createAndroidTask()

跟下 createAndroidTasks()

注意下:这里遍历了所有的variantScope,然后调用 createTasksForVariantData() 创建变体数据对应的Tasks:

跟下:createTasksForVariantScope()

抽象方法,看下哪里实现了这个方法,搜下:extends TaskManager

最终定位到了:ApplicationTaskManager 类:

噢吼,就是在这里完成APK打包过程的Tasks,可以简单跟跟验证下:createAnchorTasks(),创建锚点Tasks:

跟下:createVariantPreBuildTask()

2333,跟上面的APK打包Task链的相呼应,AGP插件的构建过程就跟到这里,接着跟下APK的完整打包Task。

0x6、Apk的打包过程

Tips:直接全局搜 "xxx", "yyy" 即可快速定位对应Task类,如 "compile", "Aidl" ~

1. compileAidl

过程简述:将.aidl 文件通过aidl工具转换成编译器能够处理的Java接口文件
相关代码:AidlCompile.java → AidlProcessor.java → call()


2. checkManifest

过程简述:检查AndroidManifest.xml文件是否存在
相关代码:CheckManifest.java


3. compileRenderscript

过程简述:编译Renderscript文件(.rs)
相关代码:RenderscriptCompile.java


4. generateBuildConfig

过程简述:生成 BuildConfig.java 文件
相关代码:GenerateBuildConfig.java


5. mainApkListPersistence

过程简述:持久化APK数据到apk-list.gson中
相关代码:MainApkListPersistence.kt


6. generateResValues

过程简述:遍历res下的values目录下xml文件,生成resValues文件:generated.xml
相关代码:GenerateResValues.java → generate() → ResValueGenerator.java


7. mergeResources

过程简述:使用AAPT2合并资源文件
相关代码:MergeResources.doFullTaskAction() → ResourceMerger.mergeData() → MergedResourceWriter.end() → mResourceCompiler.submitCompile() → AaptV2CommandBuilder.makeCompileCommand()

核心源码解析:

实现了isIncremental()方法,返回true,说明支持增量编译,跟下全量编译方法 doFullTaskAction()

  1. ResourcePreprocessor preprocessor = getPreprocessor();
  2. List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor)

接着往下走:

继续:

点进merger.mergeData() → ResourceMerger.mergeData() → DataMerger.mergeData()

呕吼,实际上调用的还是 MergedResourceWriter 类里的方法,跟下addItem():

不同文件会创建对应的 CompileResourceRequest 实例,并添加到 mCompileResourceRequests 中,后者是一个ConcurrentLinkedQueue队列,资源最后会在end()方法处处理:

最终调用 AaptV2CommandBuilder.makeCompileCommand() 方法生成aapt2命令去处理资源。

Tips:将图片转为webp格式的插件一般在此Task前处理~


8. createCompatibleScreenManifests

过程简述:manifest文件中生成compatible-screens,用于屏幕适配。
相关代码:CompatibleScreensManifest.kt


9. processManifest

过程简述:合并AndroidManifest.xml文件
相关代码:ProcessApplicationManifest.java、ProcessLibraryManifest.java


10. processResources

过程简述:调用aapt2 link 打包资源并生成R.java文件
相关代码:TaskManager.java → createProcessResTask()


11. compileKotlin

过程简述:编译Kotlin文件为字节码
相关代码:没找到...可能在kotlin插件里

12. prepareLintJar

过程简述:

全局搜,定位到 TaskManager → MAIN_PREBUILD

跟下,定位到 createTasksBeforeEvaluate()

注册了一个名为 MAIN_PREBUILD 的Task,这里没有传闭包,就是空Task,用来表明处于某种状态,即锚点。

注意,注释写道:保证MAIN_PREBUILD第一个执行,那到底是如何保证的呢?跟下 MAIN_PREBUILD 调用处:

跟下 configure(),定位到:

就是在这个方法中配置Task的Action,在这里可以拿到task实例,dependsOn保证preBuild执行在这个Task之后。

那这个Task到底是什么Task?

继续跟,

注释:配置Task Action的地方

在执行这个静态抽象Task类前调用,

register()用于创建新task,不过是延时创建,当task被需要才会创建,这里没有传task参数,说明是空task。

然后是 preDebugBuild,全局搜没搜到,而且打包链上还有一个 preReleaseBuild,明显是动态拼接而成的。在下面这里看到了一丝端倪:

VariantScope 是一个接口,并没有实现 getTaskName(),往上跟:

然后跟到:createAnchorTask()

呕吼,可以看到这里注册了很多空的task,这里的dependsOn() 是指这个任务依赖于后面的任务,执行完后面的任务才会执行此任务。

跟下哪里调用到了此方法,经过排查定位到:ApplicationTaskManager → createTasksForVariantScope()


点进去,定位到了 TransformVariantScope 接口:

空实现,点下参数prefix,看下哪里用到了,定位到了 VariantScopeImpl → getTaskName()

跟:BaseVariantData → getTaskName()

跟:VariantConfiguration → getFullName()


网上看到很多讲解APK打包过程的文章还处于 AAPT,而打包实际上用的是:AAPT2

Android Gradle插件3.0.0及更高版本默认情况下会启用AAPT2
Find in Path 勾选下 Scope,扩大搜索范围~

https://www.jianshu.com/p/839969887e2c


参考文献:

https://developer.android.google.cn/studio/command-line/aapt2
https://www.one-tab.com/page/0d9Rf99oQretVl25dRYB6A
https://www.one-tab.com/page/DEOMTd9mQM--bB9N7JOGRw

https://www.jianshu.com/p/cf90c557b866
https://www.cnblogs.com/blogs-of-lxl/p/10306145.html
https://blog.csdn.net/u010289802/article/details/105977904
http://www.zyiz.net/tech/detail-136910.html
- 【Android 修炼手册】Gradle 篇 -- Android Gradle Plugin 主要 Task 分析

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注