前言

在前一篇文章我们讲了 Groovy ,有了 Groovy 的基础就能看懂 Gradle 里的语法。但是光有 Groovy 基础还是不够的, Gradle 提供了一些 API 让我们可以很方便的参与到项目的构建中。

目录结构

在一个普通的 gradle 项目中通常会包含如下结构

1
2
3
4
5
6
gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat

gradlew

gradlew 其实就是一个脚本文件,用来执行相应的 task(后面会介绍)

gradlew 是在 LinuxmacOS 下使用的,而 gradlew.bat 则是 Windows 下的批处理文件,在 Windows 下使用。

gradlew 是和特定的版本关联在一起的,当我们有多个版本的 gralde 的时候,这样可以保证我们的项目不会因为版本的问题导致编译不了。如果关联的版本不存在, gralde 会先下载。

wrapper

gradle-wrapper.jar

gradle-wrapper.jar 是一个用来执行下载特定版本 graldejar

gradle-wrapper.properties

gradle-wrapper.properties 文件中存放了特定版本的 Gradle 的下载路径,以及本地下载好的路径。

1
2
3
4
5
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

生命周期

Gradle 中会在不同阶段做不同事情,也就是 Gradle 有自己的生命周期,分为初始化、配置、执行。

初始化阶段

对于 Android 来说,初始化阶段就是加载 settings.gralde 看看引入了哪些项目,对于引入的项目都会为其创建一个 Project 对象。

配置阶段

在配置阶段会执行每个项目下的 build.gradle 文件,根据项目的依赖生成拓补图。

执行阶段

在执行阶段会执行特定的 Task ,在这个阶段是真正干活的,会执行 Task 具体的逻辑。

生命周期的监听

gralde 执行的过程中,我们可以通过对其生命周期进行监听,方便我们在特定的时期做一些逻辑处理。

执行 gralde 任务会自动执行初始化和配置。在这个过程中我们可以对生命周期进行监听。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 初始化阶段完成的回调
this.beforeEvaluate {
    println "初始化完成"
}

// 配置阶段完成后的回调
this.afterEvaluate {
    println "配置完成"
}

// gradle 执行完毕后的监听回调
this.gradle.buildFinished {
    println "任务执行完成"
}

Project

前面提到在 settings.gralde 中使用 include 关键字引入的项目都会创建一个 projectgralde 为我们提供了许多 API 来操作 project

获取所有 Project

1
2
3
4
5
6
// 获取所有 project
def projects = getAllprojects()

projects.each {
    println "Project name is: ${it.name}"
}

输出如下所示

1
2
3
Project name is: Gradle
Project name is: app
Project name is: groovy

可以看到当前工程下有三个 Project 。其实在项目的 build.gralde 也有对所有项目进行配置的相关代码,来看一下

1
2
3
4
5
6
7
allprojects {

    repositories {
        google()
        jcenter()
    }
}

在这里为所有的项目都配置了仓库,这样我们依赖第三方的项目才知道要到哪里去下载。

这里可以直接使用 allprojects 是因为 groovy 会为默认为字段加上 getter/setter

获取子 Project

1
2
3
subprojects.each {
    println "sub project name is: ${it.name}"
}

输出结果如下

1
2
sub project name is: app
sub project name is: groovy

可以看到比之前少了 GradleProject ,这是因为 GradleRoot Project

获取父 Project

1
2
3
subprojects.each {
    println "the sub project ${it.name} parent name is: ${it.parent.name}"
}

输出结果如下

1
2
the sub project app parent name is: Gradle
the sub project groovy parent name is: Gradle

可以看到 appgroovyparent 都是 Gradle 。如果当前项目时根项目的话会返回 null

获取 Root Project

1
println "the root project name is: ${rootProject.name}"

输出结果如下

1
the root project name is: Gradle

获取指定的 Project

1
2
3
project('app') {
    println "current project name is: ${it.name}"
}

输出结果如下

1
current project name is: app

通过指定 project 的名字我们就可以获取指定的 Project

扩展属性

Project 中为我们定义了有些属性,但是这些属性太少了,我们不够用,所以 Gradle 考虑到这个问题了,为我们提供了 ext 字段,让我们可以去扩充需要的属性。我们以 compileSdkVersion 为例演示一下怎么使用。

我们在项目下新建一个 config.gralde 文件,内容如下

1
2
3
4
5
ext {
    android = [
        compileSdkVersion: 29
    ]
}

然后我们就可以在别的模块中使用了,比如在 app 模块的 build.config 中可以这么用

1
2
3
4
5
6
7
apply plugin: 'com.android.application'
apply from: "config.gradle"

android {
    compileSdkVersion rootProject.ext.android["compileSdkVersion"]

}

这样如果我们有很多模块,就可以统一配置为一个,避免了硬编码。这样不会因为要升级 sdk 的版本,把所有的模块都改一遍。

除了可以在 gralde 文件中设置,还可以在 gralde.properties 中设置,不过在 gralde.properties 中设置的类型需要强制转换成需要的类型。

1
compileSdkVersion=29

在使用的时候可以通过 hasProperty('compileSdkVersion') 来判断是否有这个属性,有的话可以通过 compileSdkVersion.toInteger 来把类型装换为需要的类型。

文件操作

1
2
3
4
5
6
// 获取根路径
println "the root dir is: ${rootDir}"
// 获取项目路径
println "the project dir is: ${projectDir}"
// 获取 build 路径
println "the build dir is: ${buildDir}"

输出结果如下

1
2
3
the root dir is: /Users/devbins/Desktop/Gradle
the project dir is: /Users/devbins/Desktop/Gradle
the build dir is: /Users/devbins/Desktop/Gradle/build

拷贝文件

1
2
3
4
copy {
    from file('build.gralde')
    into buildDir
}

通过 copy 闭包实现拷贝文件非常方便

Task

在之前我们介绍了 Gradle 生命周期,提到了 Task 会在执行阶段运行,现在就来看看。

定义 Task

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
task gradle {
    println 'hello gradle'

    doLast {
        println 'do last'
    }

    doFirst {
        println 'do first'
    }
}

Task 中,除了在 doLastdoFirst 中的代码,其余的代码都是在配置阶段执行的。 除了上面这种方式用来定义 Task ,下面这几种也是可以的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
task task1(group: "gradle", "description": "gradle1 task") {
    println 'a gradle task'
}

this.tasks.create(name: 'task2', group:"gradle") {
    println "gradle2"
}

// 方法一
task task3(dependsOn: [task1, task2]) {
    setGroup("gradle")
    setDescription("gradle3 task")
}

Task 依赖

Gradle 中有非常多 Task ,有些需要在某一个 Task 执行之后再执行特定的 Task ,这样就会形成一个依赖,我们可以通过手动来指定这些依赖。

1
2
3
4
5
6
7
// 方法一
task task4(dependsOn: [task1, task2]) {
    dependsOn task1
}

// 方法二
task4.dependsOn(task1,task5)

执行顺序

除了上面依赖的方式可以决定 Task 的调用顺序,还可以通过 mustRunAfter 来设置调用的顺序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
task task5 {
    doLast {
        println 'task5'
    }
}

task task6 {
    mustRunAfter "task5"
    doLast {
        println 'task6'
    }
}

总结

今天我们了解了 Gradle 的目录结构、生命周期、 Project 还有创建 Task ,有了这些基础后,就不会再认为 Gradle 只是个配置文件了。

我们可以根据自己的需要去改进项目的构建。

参考