Gradle之Project,Task
gradle基本概念
百度百科:Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,目前也增加了基于Kotlin语言的kotlin-based DSL,抛弃了基于XML的各种繁琐配置。
gradle是一个构建工具,也是一个编程框架
gradle组成:
- groovy核心语法
- build script block
- gradle api
gradle的优势:
- 相比于maven,ant构建更加灵活
- 可以服用各种插件
- 兼容上,兼容maven等所有功能
- 细粒度,可以自定义编译过程
gradle的生命周期
gradle的生命周期主要分为3个阶段:Initialization初始化阶段,Configuration配置阶段,Execution执行阶段
- Initialization:解析真个工程中的所有Project,构建所有Project赌赢的project对象
- Configuration:解析project中的所有的task,构建成一个有向无环图
- Execution:执行具体的task以及其依赖的task
setting.gradle:这个文件是在初始化阶段执行,一个gradle项目中必须由setting.gradle这个文件,因为它决定了那些项目参与构建。在gradle的构建中这是最先执行的一个文件。
如何监听gradle的生命周期呢
在setting.gradle文件中可以监听初始化阶段和配置之前的阶段1
2
3
4
5
6gradle.settingsEvaluated {
println('初始化阶段开始执行...')
}
gradle.beforeProject {
println('配置之前调用...')
}
在build.gradle文件中,可以监听配置之后、task的执行阶段和执行完成的阶段1
2
3
4
5
6
7
8
9
10
11
12this.afterEvaluate {
println('配置之后....')
}
gradle.taskGraph.beforeTask {
println "执行阶段 before task"
}
gradle.taskGraph.afterTask {
println "执行阶段 afterTask "
}
this.gradle.buildFinished {
println("执行阶段完毕,构建完毕...")
}
gradle Project
官方文档:https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project
在一个gradle项目中,怎么算是一个Project呢,以一个Android项目为例,跟工程算是一个Project,其内部的子module也是一个Project。准确的说,只要该文件夹下有build.gradle文件,它就是一个Project。每一个module在编译期间都会生成一个对应的project对象,我们在build.gradle中编写的代码,其实都是在一个project对象内部编写。
获取一个项目所有的project1
2
3this.getAllprojects().eachWithIndex { Project entry, int index ->
println(entry.name)
}
获取Project中的目录1
2println(this.project.getRootDir().absolutePath)
println(this.project.getBuildDir().absolutePath)
gradle中拷贝文件,非常简单,比如把app目录下的proguard-rules.pro文件拷贝到根目录下的build文件夹下面1
2
3
4copy {
from(file('proguard-rules.pro'))
into(getRootProject().getBuildDir())
}
file(‘’)函数可以定位当前project中的某个文件。
Project的依赖:
在一个Android项目的根目录中的build.gradle中,我们都会看到下面的一段配置:1
2
3
4
5
6
7
8
9
10
11
12
13buildscript {
repositories {
google()
jcenter()
maven{
url 'http://localhost:8081/xxxx'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
}
}
buildscript 就是依赖配置的核心部分,它接受一个闭包,内部可以设置很多参数,最重要的就是前面的两个repositories和dependencies。
- repositories :配置工程的仓库地址,按照顺序从不同的仓库中查找依赖。这里也可以添加本地的maven仓库地址
- dependencies :配置工程的插件依赖地址(gradle程序所依赖的第三方库)。比如
com.android.tools.build:gradle:3.5.1
就是谷歌开发的Android相关的插件还有平时开发经常用到的tinker相关插件,butterknife相关插件都在这里。
在Project中执行外部指令,比如下面的一个复制的任务:
1 | task('copytask'){ |
上面的代码:定义一个资源路径、一个目标路径和一个复制的指令。这里使用的是windows中的复制目录的指令xcopy,在mac和linux中需要使用对应系统的指令。然后使用commandLine来执行指令。
gradle Task
官方文档 :https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task
task是构建过程中的基本单元,每一个task都属于一个project,每一个task都有一个自己的名字和一个唯一的路径,该路径在所有project和所有的task中都是唯一的。
Task的创建方式:
第一种直接创建:1
2
3task('helloTask'){
println('hello task')
}
第二种通过TaskContainer容器创建1
2
3this.tasks.create('helloTask2'){
println('hello task2')
}
这两种方式创建的task没有区别,TaskContainer可以更好的管理task,它里面有创建task和查找task等方法。
Task的配置方式
同样,Task的配置也有两种方式:比如下面的两种配置分组和描述信息。1
2
3
4
5
6
7
8
9
10//第一种
task helloTask(group:'hello',description:'hello task des'){
println('hello task')
}
//第二种
this.tasks.create(name:'helloTask2'){
setGroup('hello')
setDescription('hello create task')
println('hello task2')
}
不同的task设置相同的分组,方便查找,比如把我们自己自定义的task都设置到一个同样的分组中,跟系统的区分开。
自定义Task的执行时机
当我们在studio的命令行执行前面的代码的时候,比如执行gradlew helloTask
,我们只执行第一个task,执行结果会看到helloTask和helloTask2都会输出。这是因为前面的写法都是在gradle生命周期的配置阶段执行。
那如何让一个自定义的task在gradle的执行阶段执行,可以使用doFirst和doLast这两个闭包函数。因为系统中会有很多默认的task,比如Android中build和clean等。doFirst就是在系统task执行之前执行,doLast就是在系统的task执行之后执行。
1 | task helloTask(group:'hello',description:'hello task des'){ |
通过doFirst和doLast,我们可以做一些事情,比如统计build执行阶段的时间1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18def buildStartTime
def buildEndTime
this.afterEvaluate { Project project ->
//afterEvaluate配置阶段完成后执行
def preTask = project.tasks.getByName('preBuild')
preTask.doFirst{
buildStartTime = System.currentTimeMillis()
}
def endTask = project.tasks.getByName('build')
endTask.doLast{
buildEndTime = System.currentTimeMillis()
println("build 执行时间:${buildEndTime - buildStartTime}")
}
}
//输出
> Task :app:build
build 执行时间:28018
Task的依赖
一个task可能会依赖一个或者多个别的task,那么在执行的时候,它所依赖的task会先执行
静态添加依赖,当我们明确知道需要依赖那几个task的时候1
2
3
4
5
6
7
8
9
10
11
12
13
14
15task taskA{
doLast{
println('i am taskA')
}
}
task taskC{
doLast{
println('i am taskC')
}
}
task taskB(dependsOn:[taskA,taskC]){
doLast{
println('i am taskB')
}
}
taskB依赖了taskA和taskC,运行可以看到,taskA和taskC在taskB之前执行。
动态添加依赖,比如下面的taskB依赖所有name以lib开头的task
1 | task lib1 { |
控制task的执行顺序可以通过前面的依赖的方式,也可以通过mustRunAfter这个关键字来指定某个task的执行必须在谁的后面。
1 | task taskC{ |
前面我们知道,通过this.afterEvaluate {}
这个闭包我们可以在配置完成之后来执行我们自己的一些操作,那如何将我们自己的task插入到系统构建的中间部位执行呢?
办法就是:通过mustRunAfter关键字,让我们的task必须执行在一个系统的task之后,然后通过dependsOn来让另一个一个系统的task依赖我们自己的task。这样就把我们自己的task插入到了这两个系统的task之间了。
Task的输入输出
从proguard-rules.pro中读取数据,写入到destination.txt文件中
1 | task combineFileContent { |