Gradle之Android Gradle Plugin 主要流程分析(二)
【Android 修炼手册】Gradle 篇 -- Android Gradle Plugin 主要流程分析
预备知识
- 理解 gradle 的基本开发
- 了解 gradle task 和 plugin 使用及开发
- 了解 android gradle plugin 的使用
看完本文可以达到什么程度
- 了解 android gradle plugin 的构建流程
- 了解 android gradle plugin 的主要 task 的实现
- 学会 hook android 构建流程,添加自己想要的功能
阅读前准备工作
- 项目添加 android gradle plugin 依赖
compile 'com.android.tools.build:gradle:3.0.1'
通过这种方式,可以直接依赖 plugin 的源码,读起来比较方便。
2. 官方对照源码地址 android gradle plugin 源码地址
大家可以直接 clone EasyGradle 项目,把 android-gradle-plugin-source/build.gradle 里的 implementation 'com.android.tools.build:gradle:3.0.1' 注释打开就可以了。
com.android.application 主要有下面几个流程:

一、插件启动的准备工作

implementation-class=com.android.build.gradle.AppPlugin
这里定义了入口是 AppPlugin,AppPlugin 继承自 BasePlugin。
AppPlugin 里没有做太多的操作,主要是重写了 createTaskManager 和 createExtension,剩下的大部分工作还是在 BasePlugin 里做的。
插件准备工作中主要做的事情:
1.检查插件版本
// method: BasePlugin.apply()
checkPluginVersion();
2.检查 module 是否重名
// method: BasePlugin.apply()
// 方法中会遍历所有子项目,判断是否有重复的 id
this.checkModulesForErrors();
3.初始化插件信息
// method: BasePlugin.apply()
PluginInitializer.initialize(project, this.projectOptions);
// 创建 Profiler 文件
ProfilerInitializer.init(project, this.projectOptions);
// profiler 信息中写入 plugin 版本
ProcessProfileWriter.getProject(project.getPath()).setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION).setAndroidPlugin(this.getAnalyticsPluginType()).setPluginGeneration(PluginGeneration.FIRST);
二、配置项目

配置项目这一阶段主要做的事情:
1.检查 gradle 版本是否匹配
// method: BasePlugin.configureProject()
this.checkGradleVersion();
2.创建 AndroidBuilder和 DataBindingBuilder
3.引入 java plugin 和 jacoco plugin
this.project.getPlugins().apply(JavaBasePlugin.class);
this.project.getPlugins().apply(JacocoPlugin.class);
4.设置构建完成以后的混存清理工作 添加了 BuildListener,在 buildFinished 回调里做缓存清理工作
三、配置 Extension

实现在 BasePlugin.configureExtension()
这一阶段主要做了下面几个事情:
1.创建 AppExtension,也就是 build.gradle 里用到的 android {} dsl
this.extension = this.createExtension(...);
// AppPlugin 中实现了 createExtension,创建了 android {} dsl
protected BaseExtension createExtension(...) {
return (BaseExtension)project.getExtensions().create("android", AppExtension.class, new Object[]{project, projectOptions, instantiator, androidBuilder, sdkHandler, buildTypeContainer, productFlavorContainer, signingConfigContainer, buildOutputs, extraModelInfo});
}
2.创建依赖管理,ndk管理,任务管理,variant管理
3.注册新增配置的回调函数,包括 signingConfig,buildType,productFlavor
// BasePlugin.java createExtension()
// map the whenObjectAdded callbacks on the containers.
signingConfigContainer.whenObjectAdded(variantManager::addSigningConfig);
buildTypeContainer.whenObjectAdded(
buildType -> {
SigningConfig signingConfig =
signingConfigContainer.findByName(BuilderConstants.DEBUG);
buildType.init(signingConfig);
// addBuildType,会检查命名是否合法,然后创建 BuildTypeData
variantManager.addBuildType(buildType);
});
// addProductFlavor 会检查命名是否合法,然后创建 ProductFlavorData
productFlavorContainer.whenObjectAdded(variantManager::addProductFlavor);
// VariantManager.java
// addSigningConfig 就是在 signingConfigs 里新增一个配置
public void addSigningConfig(SigningConfig signingConfig) {
this.signingConfigs.put(signingConfig.getName(), signingConfig);
}
// VariantManager.java
public void addProductFlavor(CoreProductFlavor productFlavor) {
String name = productFlavor.getName();
// checkName 会检查
checkName(name, "ProductFlavor");
if(this.buildTypes.containsKey(name)) {
throw new RuntimeException("ProductFlavor names cannot collide with BuildType names");
} else {
// 获取源码位置
DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet)this.extension.getSourceSets().maybeCreate(productFlavor.getName());
DefaultAndroidSourceSet androidTestSourceSet = null;
DefaultAndroidSourceSet unitTestSourceSet = null;
if(this.variantFactory.hasTestScope()) {
// 获取单测源码位置
androidTestSourceSet = (DefaultAndroidSourceSet)this.extension.getSourceSets().maybeCreate(computeSourceSetName(productFlavor.getName(), VariantType.ANDROID_TEST));
unitTestSourceSet = (DefaultAndroidSourceSet)this.extension.getSourceSets().maybeCreate(computeSourceSetName(productFlavor.getName(), VariantType.UNIT_TEST));
} // 创建 productFlavorData 对象
ProductFlavorData<CoreProductFlavor> productFlavorData = new ProductFlavorData(productFlavor, mainSourceSet, androidTestSourceSet, unitTestSourceSet, this.project);
this.productFlavors.put(productFlavor.getName(), productFlavorData);
}
}
4.创建默认的 debug 签名,创建 debug 和 release 两个 buildType
variantFactory.createDefaultComponents(
buildTypeContainer, productFlavorContainer, signingConfigContainer);
// ApplicationVariantFactory.java
public void createDefaultComponents(...) {
signingConfigs.create("debug");
buildTypes.create("debug");
buildTypes.create("release");
}
四、创建不依赖 flavor 的 task

先看不依赖 flavor 的 task,其实现在 TaskManager.createTasksBeforeEvaluate()。
这里主要创建了几个 task,包括 uninstallAll,deviceCheck,connectedCheck,preBuild,extractProguardFiles,sourceSets,assembleAndroidTest,compileLint,lint,lintChecks,cleanBuildCacheresolveConfigAttr,consumeConfigAttr。
这些 task 都是不需要依赖 flavor 数据的公共 task。
五、创建构建 task

在 android gradle plugin 3.x 之后,每个 flavor 必须对应一个 dimension,可以理解为 flavor 的分组,然后不同 dimension 里的 flavor 组合成一个 variant。
举个例子:
flavorDimensions "size", "color"
productFlavors {
big {
dimension "size"
}
small {
dimension "size"
}
blue {
dimension "color"
}
red {
dimension "color"
}
}
上面配置对应生成的 variant 就是 bigBlue,bigRed,smallBlue,smallRed,在这个基础上,再加上 buildTypes,就是 bigBlueDebug,bigRedDebug,smallBlueDebug,smallRedDebug,bigBlueRelease,bigRedRelease,smallBlueRelease,smallRedRelease。
createAndroidTasks 的调用时机和上面不一样,是在 project.afterEvaluate 里调用的,还记得之前文章里说道的 afterEvaluate 回调么?这个时候所有模块配置已经完成了。所以在这个阶段可以获取到对应的 flavor 以及其他配置了。
在 BasePlugin.createAndroidTasks 里,是调用 VariantManager.createAndroidTasks 完成工作的。
创建 task 的时候,会先通过 populateVariantDataList 生成 flavor 相关的数据结构,然后调用 createTasksForVariantData 创建 flavor 对应的 task。
分别看下这两个方法做的事情
1.populateVariantDataList
在方法里,会先根据 flavor 和 dimension 创建对应的组合,存放在 flavorComboList 里,之后调用 createVariantDataForProductFlavors 创建对应的 VariantData。
其中重要的几个方法:
// 创建 flavor 和 dimension 的组合
List<ProductFlavorCombo<CoreProductFlavor>> flavorComboList =
ProductFlavorCombo.createCombinations(
flavorDimensionList,
flavorDsl);
// 为每个组合创建 VariantData
for (ProductFlavorCombo<CoreProductFlavor> flavorCombo : flavorComboList) {
//noinspection unchecked
createVariantDataForProductFlavors(
(List<ProductFlavor>) (List) flavorCombo.getFlavorList());
}
创建出来的 VariantData 都是 BaseVariantData 的子类,里面保存了一些 Task,可以看一下 BaseVariantData 里的一些重要的结构,对 BaseVariantData 有个大概的了解。
public abstract class BaseVariantData implements TaskContainer {
private final GradleVariantConfiguration variantConfiguration;
private VariantDependencies variantDependency;
private final VariantScope scope;
public Task preBuildTask;
public Task sourceGenTask;
public Task resourceGenTask; // 资源处理
public Task assetGenTask;
public CheckManifest checkManifestTask; // 检测manifest
public AndroidTask<PackageSplitRes> packageSplitResourcesTask; // 打包资源
public AndroidTask<PackageSplitAbi> packageSplitAbiTask;
public RenderscriptCompile renderscriptCompileTask;
public MergeResources mergeResourcesTask; // 合并资源
public ManifestProcessorTask processManifest; // 处理 manifest
public MergeSourceSetFolders mergeAssetsTask; // 合并 assets
public GenerateBuildConfig generateBuildConfigTask; // 生成 BuildConfig
public GenerateResValues generateResValuesTask;
public Sync processJavaResourcesTask;
public NdkCompile ndkCompileTask; // ndk 编译
public JavaCompile javacTask;
public Task compileTask;
public Task javaCompilerTask; // java 文件编译
// ...
}
VariantData 里保存了很多 task,下一步就要创建这些 task。
2.createTasksForVariantData
创建完 variant 数据,就要给 每个 variantData 创建对应的 task,对应的 task 有 assembleXXXTask,prebuildXXX,generateXXXSource,generateXXXResources,generateXXXAssets,processXXXManifest 等等,重点关注几个方法:
VariantManager.createAssembleTaskForVariantData() // 创建 assembleXXXTask
TaskManager.createTasksForVariantScope() // 是一个抽象类,具体实现在 ApplicationTaskManager.createTasksForVariantScope()
TaskManager.createPostCompilationTasks() // 创建 .class to dex 的 task, 创建 transformTask,我们创建的 transform 就是这个阶段添加进来的,是在 addCompileTask 里调用的 // createTasksForVariantScope 是一个抽象方法,具体实现在子类中,可以看一下 ApplicationTaskManager.createTasksForVariantScope()
// createTasksForVariantScope 里的实现,如果在业务中有需要查看相关 task 源码时,可以来这里找
void createTasksForVariantScope() {
this.createCheckManifestTask(tasks, variantScope); // 检测 manifest
this.handleMicroApp(tasks, variantScope);
this.createDependencyStreams(tasks, variantScope);
this.createApplicationIdWriterTask(tasks, variantScope); // application id
this.createMergeApkManifestsTask(tasks, variantScope); // 合并 manifest
this.createGenerateResValuesTask(tasks, variantScope);
this.createRenderscriptTask(tasks, variantScope);
this.createMergeResourcesTask(tasks, variantScope, true); // 合并资源文件
this.createMergeAssetsTask(tasks, variantScope, (BiConsumer)null); // 合并 assets
this.createBuildConfigTask(tasks, variantScope); // 生成 BuildConfig
this.createApkProcessResTask(tasks, variantScope); // 处理资源
this.createProcessJavaResTask(tasks, variantScope);
this.createAidlTask(tasks, variantScope); // 处理 aidl
this.createShaderTask(tasks, variantScope);
this.createNdkTasks(tasks, variantScope); // 处理 ndk
this.createExternalNativeBuildJsonGenerators(variantScope);
this.createExternalNativeBuildTasks(tasks, variantScope);
this.createMergeJniLibFoldersTasks(tasks, variantScope); // 合并 jni
this.createDataBindingTasksIfNecessary(tasks, variantScope); // 处理 databinding
this.addCompileTask(tasks, variantScope);
createStripNativeLibraryTask(tasks, variantScope);
this.createSplitTasks(tasks, variantScope);
this.createPackagingTask(tasks, variantScope, buildInfoWriterTask); // 打包 apk
this.createLintTasks(tasks, variantScope); // lint
} // createPostCompilationTasks 实现:
// 处理 Android Transform
void createPostCompilationTasks() {
for (int i = 0, count = customTransforms.size(); i < count; i++) {
Transform transform = customTransforms.get(i);
// TransformManager.addTransform 实际上是为 transform 创建了一个 Task
transformManager
.addTransform(tasks, variantScope, transform)
.ifPresent(t -> {
if (!deps.isEmpty()) {
t.dependsOn(tasks, deps);
}
// if the task is a no-op then we make assemble task depend on it.
if (transform.getScopes().isEmpty()) {
variantScope.getAssembleTask().dependsOn(tasks, t);
}
});
}
}
六、总结
android gradle plugin 的主要流程基本上就结束了,主要流程图如下所示

这里总结几个要点:
- com.android.application 入口类是 AppPlugin,但大部分工作都是在 BasePlugin 里完成的
- build.gradle 里见到的 android {} dsl 是在 BasePlugin.configureExtension() 里声明的
- 主要的 task 是在 BasePlugin.createAndroidTasks() 里生成的
- 主要 task 的实现可以在 TaskManager 中找到
- transform 会转化成 TransformTask
Gradle之Android Gradle Plugin 主要流程分析(二)的更多相关文章
- Gradle之Android Gradle Plugin 主要 Task 分析(三)
[Android 修炼手册]Gradle 篇 -- Android Gradle Plugin 主要 Task 分析 预备知识 理解 gradle 的基本开发 了解 gradle task 和 plu ...
- 【Gradle】Android Gradle 插件
Android Gradle 插件 Android Gradle 插件简介 从Gradle角度来看,Android其实是Gradle的一个第三方插件,它是由Google的Android团队开发的.但从 ...
- 【Gradle】Android Gradle 多渠道构建
Android Gradle 多渠道构建 多渠道构建的基本原理 在Android Gradle中,定义了一个叫Build Variant的概念,一个Build Variant=Build TYpe+P ...
- 【Gradle】Android Gradle 多项目构建
Android Gradle 多项目构建 Android 项目区别 Android项目一般分为库项目,应用项目,测试项目,Android Gradle 根据这些项目分别对应3种插件:com.andro ...
- Android 4.4KitKat AudioRecord 流程分析
Android是架构分为三层: 底层 Linux Kernel 中间层 主要由C++实现 (Android 60%源码都是C++实现) 应用层 主要由JAVA开发的应用程序 应用程序执行 ...
- Android 4.4 音量调节流程分析(二)
之前在Android 4.4 音量调节流程分析(一)里已经有简单的分析音量控制的流程,今天想接着继续分析下音量大小计算的方法.对于任一播放文件而言其本身都有着固定大小的音量Volume_Max,而在A ...
- Cocos2d-x3.3RC0的Android编译Activity启动流程分析
本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...
- Android 7.1 WindowManagerService 屏幕旋转流程分析 (二)
一.概述 从上篇[Android 7.1 屏幕旋转流程分析]知道实际的旋转由WindowManagerService来完成,这里接着上面具体详细展开. 调了三个函数完成了三件事,即首先调用update ...
- 【Gradle】Android Gradle 高级自定义
Android Gradle 高级自定义 使用共享库 Android的包,如android.app,android.content,android.view,android.widget等,是默认包含 ...
随机推荐
- Linux学习--第十天--bash脚本、用户自定义变量、环境变量、位置参数变量、预定义变量、标准输入输出、wc、history、dd、PS1
shell简介 分为两种c shell 和b shell b shell:sh.ksh.Bash.psh.zsh: (Bash和sh兼容,linux基本shell是Bash) c shell:csh. ...
- 总结下Nginx的功能模块
nginx-1.10.3]# ./configure \ --prefix=/usr/local/nginx \ #指定安装路径 --user=nginx --group=ngin ...
- RedHat7安装yum工具
一 RedHat7安装yum工具 1.1 查看yum是否可用 yum list yum repolist 1.2 卸载原yum rpm ...
- SpringBoot02——A Simple SpringBoot Project&Hot Deployment
1.简单的Controller映射 1.新建一个controller包,包一定在启动器的下一层级 2.可以在application.properties中进行调整端口和context-path参数 s ...
- C# WinFrom自适应
1.在窗体的Project中建一个自适应的类. 2.类的代码如下 public class AutoSizeForm { //(1).声明结构,只记录窗体和其控件的初始位置和大小. public st ...
- 重大更新:DeepFaceLab更新至2019.12.20
本次更新增加SAEHD:lr_dropout参数,训练时可以打开或者禁用(默认禁用),每次换脸经过足够的训练后可以启用此选项以减少重复次数,从而获得额外的清晰度.还有一个比较有意义的更新是增加了图片元 ...
- 【BZOJ1999】树网的核
题目大意:题目过长,无法简单描述... 题解: 由于树网的核一定是树直径的一段,因此考虑先将直径取出,通过两次 BFS 即可.要求的东西是树上任意一点到这条取出的线段的距离的最大值,发现这个最大值有可 ...
- pt-align的用法简要记录
pt-align的用法简要记录 1.pt-align 功能:将其它工具的输出按列对齐用法:pt-align [FILES]如果没有指定文件,则默认读取标准输入的内容. 2.例如: [root@dbte ...
- 常用深度学习框架(keras,pytorch.cntk,theano)conda 安装--未整理
版本查询 cpu tensorflow conda env list source activate tensorflow python import tensorflow as tf 和 tf.__ ...
- 实战build-react(二)-------引入Ant Design
安装 Ant Design npm install antd --save 或 yarn add antd 注释:https://www.jianshu.com/p/21caf40ee93e(cop ...