从Gradle生命周期到自定义Task挂接到Build构建流程全解

include ‘:myjson’ // 需要参与构建的模块名称

project(‘:myjson’).projectDir = file(‘/Users/WorkSpace/AndroidDemo/MyJson/myjson’)

  • include方法 :指定参与构建的模块的名称,模块名前需要加冒号(:),模块名称可以任取。

  • project方法:加载指定的模块,并为该模块设置一个工程路径,参数必须与include参数一致 。

这样本工程就可以引用到其它位置的模块了,如果引用的是一个 library 模块,那么在本工程的模块中,就可以依赖这个 library 模块了,如:

implementation project(“:myjson”)

1-2、监听初始化过程

在上面的 Settings 类的方法列表中,有一个 getGradle() 方法,返回一个 Gradle 对象,通过这个 Gradle 对象,我们可以监听 Gradle 构建过程中 各个生命周期方法的回调,如在 settings.gradle 文件中,增加如下监听:

gradle.addBuildListener(new BuildListener() {

void buildStarted(Gradle var1) {

println ‘buildStarted()->开始构建’

}

void settingsEvaluated(Settings var1) {

println ‘settingsEvaluated()->settings评估完成(settins.gradle中代码执行完毕)’

// var1.gradle.rootProject 这里访问Project对象时会报错,还未完成Project的初始化

}

void projectsLoaded(Gradle var1) {

println ‘projectsLoaded()->项目结构加载完成(初始化阶段结束)’

println ‘projectsLoaded()->初始化结束,可访问根项目:’ + var1.gradle.rootProject

}

void projectsEvaluated(Gradle var1) {

println ‘projectsEvaluated()->所有项目评估完成(配置阶段结束)’

}

void buildFinished(BuildResult var1) {

println 'buildFinished()->构建结束 ’

}

})

在 build.gradle 文件中,也可以拿到 Gradle 对象,但如果在 build.gradle 文件中添加上面的监听事件的话,buildStartedsettingsEvaluatedprojectsLoaded 方法是不会回调的,因为这三个方法是在初始化阶段 执行 settings.gradle 文件的时候执行的,但另外两个方法是会回调的。

在根工程的 build.gradle 文件中,增加如下代码,以便更好的观察上面添加的监听事件的回调时机:

allprojects {

afterEvaluate {

println “${name}:配置完成”

}

}

打印信息如下:

settingsEvaluated()->settings评估完成(settins.gradle中代码执行完毕)

projectsLoaded()->项目结构加载完成(初始化阶段结束)

projectsLoaded()->初始化结束,可访问根项目:root project ‘KotlinLearning’

Configure project :

KotlinLearning:配置完成

Configure project :app

app配置完成

Configure project :kotlinlearning

kotlinlearning配置完成

projectsEvaluated()->所有项目评估完成(配置阶段结束)

buildFinished()->构建结束

2、配置阶段



配置阶段的任务:是执行各项目下的build.gradle脚本,完成 Project 的配置,并且构造Task任务依赖关系图以便在执行阶段按照依赖关系执行Task

2-1、配置阶段执行的代码

配置阶段也是我们最常接触到的构建阶段,比如应用外部构建插件apply plugin: 'com.android.application',配置插件的属性android{ compileSdkVersion 25 ...}等。

每个build.gralde脚本文件对应一个Project对象,在初始化阶段创建,Project接口文档。 配置阶段执行的代码包括:

  • build.gralde中的各种语句

  • 闭包

  • Task中的配置段语句

验证:在根目录的build.gradle中添加如下代码:

println ‘build.gradle的配置阶段’

// 调用Project的dependencies(Closure c)声明项目依赖

dependencies {

// 闭包中执行的代码

println ‘dependencies中执行的代码’

}

// 创建一个Task

task test() {

println ‘Task中的配置代码’

// 定义一个闭包

def a = {

println ‘Task中的配置代码2’

}

// 执行闭包

a()

doFirst {

println ‘这段代码配置阶段不执行’

}

}

println ‘我是顺序执行的’

打印信息如下:

build.gradle的配置阶段

dependencies中执行的代码

Task中的配置代码 Task中的配置代码2 我是顺序执行的

从上面的打印信息可以看出,在执行了dependencies的闭包后,直接执行的是任务test中的配置段代码(Task中除了Action外的代码段都在配置阶段执行)

需要注意的是,执行任何 Gradle 命令,在初始化阶段和配置阶段的代码都会被执行。

2-2、Task 依赖关系配置完成监听

配置阶段另外一个重要的任务就是 构建 Task 依赖关系的有向无环图,说简单一点,就是给 所有的 Task 排一个执行顺序,在执行阶段的时候,就按照这个顺序去执行所有的 Task 任务。

我们可以通过 Gradle 对象的 getTaskGraph 方法来得到该有向无环图对象: TaskExecutionGraph,通过TaskExecutionGraph类的相关方法可以监听 Task 依赖关系构建完成的通知,如:

void whenReady(Closure var1)

void addTaskExecutionGraphListener(TaskExecutionGraphListener var1)

在 build.gradle 文件中,增加如下代码:

gradle.getTaskGraph().whenReady {

println “whenReady Task依赖关系构建完成,size=${it.allTasks.size()}”

it.allTasks.forEach { task ->

println “${task.name}”

}

}

gradle.getTaskGraph().addTaskExecutionGraphListener(new TaskExecutionGraphListener() {

@Override

void graphPopulated(TaskExecutionGraph graph) {

println “graphPopulated Task依赖关系构建完成 size=${graph.allTasks.size()}”

graph.allTasks.forEach { task ->

println “${task.name}”

}

}

})

点击运行按钮,运行app的时候,就可以打印出 Task 任务列表

whenReady Task依赖关系构建完成,size=40 preBuild preDebugBuild compileDebugAidl compileDebugRenderscript generateDebugBuildConfig checkDebugAarMetadata generateDebugResValues generateDebugResources mergeDebugResources createDebugCompatibleScreenManifests extractDeepLinksDebug processDebugMainManifest processDebugManifest processDebugManifestForPackage processDebugResources compileDebugKotlin javaPreCompileDebug compileDebugJavaWithJavac compileDebugSources mergeDebugNativeDebugMetadata mergeDebugShaders compileDebugShaders generateDebugAssets mergeDebugAssets compressDebugAssets processDebugJavaRes mergeDebugJavaResource checkDebugDuplicateClasses dexBuilderDebug desugarDebugFileDependencies mergeExtDexDebug mergeDexDebug mergeDebugJniLibFolders mergeDebugNativeLibs stripDebugDebugSymbols validateSigningDebug writeDebugAppMetadata writeDebugSigningConfigVersions packageDebug assembleDebug graphPopulated Task依赖关系构建完成 size=40 preBuild 。。。。。 assembleDebug

3、执行阶段



执行阶段就是根据配置阶段构建的 Task 依赖关系去执行相关的 Task。

当我们运行项目的时候,Gradle 就会根据 Task的依赖关系依次去执行相关的Task。还可以通过 gradle 命令去执行指定的 Task,如在控制台中输入如下命令,就可以执行 build 任务:

./gradlew build

build 是任务名称。

二、Gradle Hook点

============================================================================


Gradle 提供了非常多的接口回调以便我们修改构建过程中的行为,整体流程如下图所示:

方法说明:

  • Gradle#settingsEvaluated 方法:与BuildListener的settingsEvaluated的执行时机一样,需要在 settings.gradle 文件中添加,否则无效。

  • Gradle#projectsLoaded 方法:与BuildListener的projectsLoaded的执行时机一样,需要在 settings.gradle 文件中添加,否则无效。

  • Gradle#beforeProject方法,在每个工程 配置之前执行,

  • Project#beforeEvaluate方法,在调用该方法的 Project 配置之前执行,如果在 module 的 build.gradle 文件中调用该方法,是不会执行的,因为执行到build.gradle的时候,已经过了beforeEvaluate执行的时间节点了。

  • Gradle#afterProject方法,在每个工程配置完成之后,执行该方法,哪怕构造过程出错,也会调用,截止到出错的文件。

  • Project#afterEvaluate方法,在调用该方法的Project 配置执行完成之后调用,在这个时机,该工程就配置完成了,如在这个方法回调中就可以获取到所有的Task任务了。

  • Gradle#projectsEvaluate方法,当所有的工程都配置完成之后,执行该方法,与BuildListener的projectsEvaluated方法的执行时机是一样的。

三、指定 Task 执行顺序

============================================================================


在Gradle中,有三种方式指定task的执行顺序:

  • dependsOn 强依赖方式

  • 通过Task输入输出

  • 通过API指定执行顺序

3-1、通过dependsOn强依赖方式指定


dependsOn 强依赖的方式可以细分为 静态依赖动态依赖

  • 静态指定依赖:在创建task的时候,就明确的知道定义的 Task需要依赖的task是什么,直接通过 dependsOn 参数或者 dependsOn 方法指定所依赖的Task。

task提供了dependsOn,finalizedBy方法来管理task之间的依赖关系,依赖关系表达的是执行这个task时所需要依赖的其他task,也就是说这个task不能被单独执行,执行这个 Task 之前或之后需要执行另外的task。

  • 动态指定依赖:在创建 Task的时候,不知道需要依赖哪些 Task,通过 dependsOn 方法动态依赖符合条件的 Task

示例代码如下所示:

静态指定依赖

在定义 Task 的时候,直接为 Task 指定 dependsOn 参数,值为 另一个被依赖的 Task 的名字:

task taskX {

doLast{

println ‘taskX’

}

}

task taskY {

doLast{

println ‘taskY’

}

}

task taskZ(dependsOn:taskX) { // 多依赖方式:dependsOn:[taskX,taskY]

doLast{

println ‘taskZ’

}

}

// 当我们执行taskZ的时候,由于依赖了taskX,则taskX会先执行,然后才会执行:taskZ

除了在 Task 定义的时候指定 Task 的依赖之外,还可以通过 TaskdependsOn 方法,为 Task 指定依赖:

task taskZ {// 定义Task的时候不指定依赖

doLast{

println ‘taskZ’

}

}

// 通过task的dependsOn方法,也可以指定task的依赖task。

taskZ.dependsOn(taskX,taskY)

当一个task依赖多个task的时候,被依赖的task之间,如果没有依赖关系的话,那么他们的执行顺序是随机的,并无影响。

动态添加依赖

当 Task 在定义的时候,不知道所依赖的 Task 是什么,在配置阶段,通过条件找出符合条件的 Task ,并进行依赖。

task lib1 {

doLask{

println ‘lib1’

}

}

task lib2 {

doLask{

println ‘lib2’

}

}

task lib3 {

doLask{

println ‘lib3’

}

}

// 动态指定taskX依赖所有以lib开头的task

task taskX{

// 动态指定依赖

dependsOn this.tasks.findAll{ task->

return task.name.startsWidth(‘lib’)

}

doLast {

println ‘taskZ’

}

}

3-2、通过Task输入输出指定


当一个参数作为TaskA的输出参数,同时又作为TaskB的输入参数。那么当执行TaskB的时候先要执行TaskA。即输出的Task先于输入的Task执行。 如:

ext {

testFile = file(“${this.buildDir}/test.txt”)

}

// 生产者 Task

task producer {

outputs.file testFile

doLast {

outputs.getFiles().singleFile.withWriter { writer ->

writer.append(“我爱中国”)

}

println “producer Task 执行结束”

}

}

// 消费者 Task

task consumer {

inputs.file testFile

doLast {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

代码真的是重质不重量,质量高的代码,是当前代码界提倡的,当然写出高质量的代码肯定需要一个相当高的专业素养,这需要在日常的代码书写中逐渐去吸收掌握,谁不是每天都在学习呀,目的还不是为了一个,为实现某个功能写出高质量的代码。

所以,长征路还长,大家还是好好地做个务实的程序员吧。

最后,小编这里有一系列Android提升学习资料,有兴趣的小伙伴们可以来看下哦~

mg-Q7kfVM3t-1712056401289)]
[外链图片转存中…(img-lueai4H5-1712056401289)]
[外链图片转存中…(img-JIX5HUBk-1712056401290)]
[外链图片转存中…(img-fcQ3LOgT-1712056401290)]
[外链图片转存中…(img-mNzYUTDF-1712056401290)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-qHofbc7C-1712056401290)]

最后

代码真的是重质不重量,质量高的代码,是当前代码界提倡的,当然写出高质量的代码肯定需要一个相当高的专业素养,这需要在日常的代码书写中逐渐去吸收掌握,谁不是每天都在学习呀,目的还不是为了一个,为实现某个功能写出高质量的代码。

所以,长征路还长,大家还是好好地做个务实的程序员吧。

最后,小编这里有一系列Android提升学习资料,有兴趣的小伙伴们可以来看下哦~

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值