Gradle学习之核心要点
概述
目前Android Studio中建立的工程都是基于gradle进行构建的,Gradle框架是使用Groovy语言实现的,关于Groovy语言的学习将不再赘述可以参考(精通Groovy),不光是在项目工程配置中通过配置gradle,比如extention、SourceSet;打包生成apk过程,目前很多技术领域开始使用Gradle的plugin 在transform阶段做些特殊操作,比如模块化、热修复、SPI的优化等等。
重要的概念
在进行本文之前,首先来了解Gradle中两个核心的概念
Project
每次构建至少由一个project构成,项目结构中的每个build.gradle文件代表一个projectTask
一个project由一到多个task构成。task又可以细分为3大类,增量、非增量、Transform,,在这编译脚本文件中可以定义一系列的task;task 本质上又是由一组被顺序执行的Action对象构成,Action其实是一段代码块,类似于Java中的方法(transform比较特殊点,这个后面再介绍)
Project为Task提供了执行上下文。 我们在打包执行assembleDebug 也是触发Gradle的一个个内置Task,最终将Apk生成
Gradle 构建生命周期
Gradle 也像Activity一样具备有生命周期主要分为3个核心阶段
- 初始化阶段
- 配置阶段
- 执行阶段
下面将深入分析一下各个阶段所做的事情
三个阶段
每次构建的执行本质上是执行一系列的task,并且某些task还需要依赖其他task,这些task的依赖关系都是在构建阶段确定的。每次构建分为3个阶段(Build phases 文档 )
- Initialization: 初始化阶段
初始化阶段会执行项目根目录下的settings.gradle文件,它用于告诉构建系统哪些模块需要包含到构建过程中,并根据每个build.gradle文件创建一个个与之对应的Project实例。
- Configuration:配置阶段
这个阶段,会解析Preoject对象,下载依赖文件,通过执行构建脚本来为每个project创建并配置Task,根据task的执行顺序构建Task的有向无环图。主要包括 build.gradle中的配置(如android、plugin、dependencies)、Task配置。在配置完成后,整个项目工程的Task依赖关系都已经确定了(Gradle对象的getTaskGraph对象访问Task,对应TaskExecutionGraph)
- Execution:执行阶段
这是Task真正被执行的阶段,我们所有的构建,编译,打包,debug,test等都是执行了某一个task
比如我们执行一个打包命令,经历了初始化解析settings文件,配置解析project构建task执行,可以看到执行如下关键task
./gradlew app:assembleDebug –console=plain
1 | Task :app:preBuild UP-TO-DATE |
可以apk构建是非常解耦的,各个Task各司其职,后续升级迭代也非常的方便,而且开发者需要自己定义Task也是可以
监听生命周期
在gradle的构建过程中,在关键的声明周期上 gradle为我们提供了钩子,帮助我们针对项目的需求定制构建的逻辑,网上有一些比较详细的图,如下图所示:
网上有个比较详细的图 借用一下


可以看到,整个Gradle生命周期的流程包含如下四个部分
- 解析settings.gradle来获取模块信息 (初始化)
- 配置每个模块,此时只是解析project,构建Task图,配置完了之后有个重要的回调 project.afterEvaluate 表示配置完成可以执行task
- 执行task
我们可以在生命周期的关键节点 做一些监听
关于可用的钩子可以参考Gradle和Project中的定义,常用的钩子包括:
Gradle
beforeProject()/afterProject()
等同于Project中的beforeEvaluate和afterEvaluatesettingsEvaluated()
settings脚本被执行完毕,Settings对象配置完毕projectsLoaded()
所有参与构建的项目都从settings中创建完毕projectsEvaluated()
所有参与构建的项目都已经被评估完
TaskExecutionGraph
- whenReady()
task图生成。所有需要被执行的task已经task之间的依赖关系都已经确立
Project
- beforeEvaluate()
- afterEvaluate()
可以搜下项目中的配置加深体会。
Project
Project 是与Gradle交互的主接口,比如我们在自定义Plugin时候实现Plugin接口将阐述传给
1 | void apply(Project project) |
通过Project可以使用gradle的所有特性。前面也提到在初始化阶段 build解析,它本质上就是project对象的委托,在脚本中的配置方法都对应着Project中的API,当构建进程启动后Gradle基于build.gradle中的配置实例化org.gradle.api.Project类,本质上可以认为是包含多个Task的容器,所有的Task都存放在TaskContainer中,Project对象的类图如下所示

从上图可以了解到ProjectApi主要分为以下部分
- Project API: 让当前的Project拥有操作它的父Project以及管理它的子Project的能力,以及相关属性
- Task相关API: 为当前Project提供了新增Task以及管理已有Task的能力
- File相关API:主要用来操作当前Project下的一些文件处理
- Gradle 生命周期API
Project API
先看一个项目结构

- getRootProject
获取根Project对象
1 | println getRootProject() |
- getRootDir方法
获取根目录文件夹路径
1 | println getRootDir() |
- getBuildDir方法
获取当前Project的build文件夹路径
1 | println getBuildDir() |
getParent方法
获取当前父Parent对象1
2
3
4
5println getParent()
//打印结果
> Configure project :app
root project 'GradleDemo'getAllprojects方法
获取当前Project及其子Project对象,返回值是一个Set集合1
2
3
4
5
6//当前在根工程的 build.gradle 文件下
println getAllprojects()
//打印结果
> Configure project :
[root project 'GradleDemo', project ':app']或者使用其闭包形式
1 | allprojects { |
我们通常会使用闭包的语法在根 build.gradle 下进行相关的配置,如下:
1 | allprojects { |
- getSubprojects方法
获取当前Project下的所有子Project对象,返回值是一个Set集合
1 | //当前在根工程的 build.gradle 文件下 |
同样也可以使用其闭包形式
1 | subprojects { |
扩展属性
有2种定义方式
ext关键字
1
2
3
4
5
6
7
8//当前在根 build.gradle 下
//方式1:ext.属性名
ext.test = 'test000'
//方式2:ext 后面接上一个闭包
ext{
test1 = 'test111'
}在gradle.properties下定义扩展属性
1 | test2=test222 |
文件操作 API
主要也分为2大类
- 路径获取
- 文件操作
包括 文件定位、文件拷贝、文件树遍历
文件定位
接收一个相对路径从当前project工程开始查找,如果我们通过new File的方式需要传入一个绝对路径1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//============================== 1、file 方法应用============================
//通过 file 方法传入一个相对路径,返回值是一个 file 对象
println file('../config.gradle').text
//通过 new File 方式传入一个绝对路径
def file = new File('/Users/xsf/learning/GradleDemo/config.gradle')
println file.text
//上述两者打印结果相同
//============================== 2、files 方法应用============================
//通过 files 方法传入多个相对路径,返回值是一个 ConfigurableFileCollection 即文件集合
files('../config.gradle','../build.gradle').each {
println it.name
}
//打印结果
> Configure project :app
config.gradle
build.gradlecopy文件拷贝
Project对象提供了copy方法,使得我们拷贝文件或者文件夹变得十分简单,该方法接受一个闭包,具体可以参考传送门
文件拷贝
1 | //1、传入路径 |
文件夹拷贝(如果没有会创建)
1 | copy { |
- fileTree文件树映射
该方法十分方便的讲一个目录转变成文件树,比如变量根目录gradle文件夹,打印文件以及文件夹名称
1 | fileTree('../gradle/'){ FileTree fileTree -> |
通常我们在build.gradle中看到如下的配置
1 | implementation fileTree(include: ['*.jar'], dir: 'libs') |
其实就是调用了fileTree接收map参数的重载方法
1 | ConfigurableFileTree fileTree(Map<String, ?> var1); |
作用就是引入当前project目录下的libs文件夹下的所有jar包
- exec外部命令执行
Project对象提供了exec方法,方便我们执行外部的命令,比如一些git操作,文件移动等等
1 | task taskMove() { |
Task
Task是gradle执行的一个任务,gradle将一个个Task串起来完成具体的任务构建,task是gradle的原子性操作,简单理解为task是Gradle脚本中的最小可执行单元,下面是Task的类图

Task介绍
- Task的几种常见写法
1 | task myTask1 { |
- doFirst 表示:Task 执行最开始时被调用的 Action
- doLast 表示: task 执行完时被调用的 Action
上面都是在gradle 简单创建的task,通常我们在自定义Plugin时需要自定义继承Task通过注解@TaskAction来表示自定义Task,
1 |
|
关于自定义Task下文会有详细介绍。
除了这些默认的 Gradle还内置了一些常用的类型,比如
- Copy
- Delete
- Sync task
更多类型可以参考官方文档 传送门
1 |
|
在 Gradle 中定义 Task 的时候,可以指定更多的参数,如下所示:
| 参数名 | 含义 | 默认值 |
|---|---|---|
| name | task的名字 | 必须指定,不能为空 |
| type | task的父类 | 默认值为org.gradle.api.DefaultTask |
| overwrite | 是否替换已经存在的同名task | false |
| group | task所属的分组名 | null |
| description | task的描述 | null |
| dependsOn | task依赖的task集合 | 无 |
| constructorArgs | 构造函数参数 | 无 |
- Task 依赖
gradle中任务的顺序是不确定的,通过task之间的依赖关系,gradle在配置阶段就能准确的构建出有向无环图,使用task的dependsOn()方法,允许我们为task声明一个或者多个task依赖。
1 |
|
Task 管理
在前面的 Project Api类图中我们可以看到Project.getTasks()来获取TaskContainer实例,该类
是可以用来管理当前Task
1 |
|
自定义Task
Gradle 中通过 task 关键字创建的 task,默认的父类都是 org.gradle.api.DefaultTask,这里定义了一些 task 的默认行为。看看下面这个例子:
1 |
|
Gradle所说的Task是org.gradle.api.Task接口,默认实现是org.gradle.api.DefaultTask类,其类图如下

从这里可以看到丰富的操作Task的API,除了DefaultTask类外
自定义Task挂载到构建流程
从前文我们知道gradle提供了一系列生命周期让我们去操作,我们可以在需要的时刻挂在自己需要的task,通常有2种方式
- 通过dependsOn
让构建流程中的Task依赖我们自定义的Task
1 | task myCustomTask{ |
执行下面的命令查看 tree
1 | ./gradlew build taskTree --no-repeat |

build一下 就可以看到Task打印的日志了
- 通过 finalizedBy指定
这个关键字时表示在某个task执行完成之后,指定需要执行的Task
1 | task myCustomTask{ |
- mustRunAfter配合dependsOn指定
在2个Task之间插入自定义的Task
1 | task myCustomTask{ |
上述Task依赖变化过程:
1 | processDebugResources -> mergeDebugResources |
小结
本文主要介绍了
- gradle生命周期
- Project
- Task
- 自定义Task 挂载到构建流程的3种方式
感谢阅读希望能给你带来帮助~









