一、前言
在编写Gradle脚本中,经常会使用到 Task,自定义Task的实现方法也有很多,最简单的就是在Gradle脚本中直接使用闭包的方式添加功能代码,这种类型的Task在实现一次性执行的操作非常方便,若这种方法无法满足要求时(或者为了实现更好的封装),就可以自定义 Task 类。自定义 Task 类的还有一个好处就是,可以将你定义的Task 类,封装成插件包,发布到仓库,在其他项目直接依赖使用。
二、自定义Task类的封装
首先,我们先来了解下如何将自定义的Task类封装起来,这里总结起来有三种:
2.1 在构建脚本中(build script)
你可以直接在构建脚本中声明Task类,这样做的一个好处就是,你无需做任何事情,Task类就会自动编译并添加到构建脚本的类路径中。然而,在构建脚本中定义的Task类,只能在当前构建脚本中可见,因此,无法在定义它的构建脚本之外复用。
2.2 在当前项目源码中(buildSrc project)
你可以将 Task 类的源码放到项目的源代码目录中。Gradle 将会完成对Task类源码的编译测试,并让其在构建脚本的类路径中生效。Task 类将会在项目中的所有构建脚本中可用,但是对于项目意外的的项目构建,依旧不可用,因此你无法在外部项目构建中复用这些 Task 类(当然,你可以将这些 Task 类源码拷贝到其他项目,但是这就造成了代码的冗余等问题)。将 Task 类放到项目源码中,也实现了将任务声明(即任务应该做什么)与任务实现(即任务如何执行)分开。
特别说明:
将 Task 类放到项目源码中,根据语言的不同,目录有所不同。
1)基于groovy
语言,放到rootProjectDir/buildSrc/src/main/groovy
目录;
2)基于java
语言,放到rootProjectDir/buildSrc/src/main/java
目录;
3)基于kotlin
语言,放到rootProjectDir/buildSrc/src/main/kotlin
目录。
2.3 在独立的项目中(standalone project)
在上面的介绍中,既然 Task 类可以放到项目源码中,那么当然可以创建一个单独项目专门用于 Task 类源码的开发与管理,这个项目可以产出 并发布jar 文件,供其他项目使用。通常,这个jar库文件会包含一些自定义的插件,或打包一些关联的 Task 类,或两者都包含。你还可以将产出的 jar 文件发布到远程仓库中,其他项目可以直接通过远程仓库依赖引用,这个我们将在后面详细介绍。
三、自定义Task类
2.1 编写一个简单的 Task 类
自定义Task类,你可通过继承 Gradle 中的 DefaultTask
类实现。如下示例:
abstract class CheckingTask extends org.gradle.api.DefaultTask {}
注意事项:自定义的 Task 类必须是可以继承的,也就是说自定义的 Task 类不能是 final 的。如果你使用 Kotlin 语言开发,要特别注意 kotlin 语法中,类定义默认是 final 的(Java 语法中,如果没有指定是 final,都不是 final),因此我们在定义 Task 类的时候,习惯性将 Task 类定义为 抽象类 (abstract),这样就可以确保 Task 类不是 final 的。
2.2 给 Task 类添加行为动作
上面的示例中,Task 类没有做任何事情,可以通过在 Task 类中添加使用 TaskAction
注解标注的成员方法来添加行为动作。在 Task 执行过程中,Gradle 会自动识别这些成员方法并执行相关的行为动作。当然,除了添加成员方法之外,你还可以在构建Task对象的时候,通过 doFirst
和 doLast
闭包,向任务中添加行为动作,如下示例:
abstract class CheckingTask extends org.gradle.api.DefaultTask {@org.gradle.api.tasks.TaskActiondef checkDisk() {println "CheckingTask -- checking disk"}
}
tasks.register('doChecking', CheckingTask) {// 构建任务的时候,通过doFirst闭包添加行为动作doFirst {println "Start to check you computer"}doLast {println "Check computer finished"}
}
gradle doChecking -q
输出
Start to check you computer
CheckingTask -- checking disk
Check computer finished
2.3 给 Task 类添加属性
我们可以向 Task 类中添加属性,这样就可以实现对 Task 的自定义。任务其实就是一个对象,我们可以通过对象设置它的属性和调用它的方法。如下示例:
abstract class CheckingTask extends org.gradle.api.DefaultTask {@org.gradle.api.tasks.Inputabstract org.gradle.api.provider.Property<String> getOwner() // 添加一个owner属性CheckingTask() {// 构造函数中设置默认值owner.convention("Unknown")}@org.gradle.api.tasks.TaskActiondef checkDisk() {println "CheckingTask -- checking disk -- ${owner.get()}"}
}
tasks.register('doChecking', CheckingTask) {owner = "Mr Zhang"doFirst {println "Start to check you computer"}doLast {println "Check computer finished"}
}
gradle doChecking -q
输出
Start to check you computer
CheckingTask -- checking disk -- Mr Zhang
Check computer finished
2.4 在独立项目中开发自定义 Task 类
前面我们知道可以在一个独立的项目中开发 Task 类,打包和发布到仓库,这样就可以共享给其他项目使用,下面我们将详细讲解一下如何实现。
首先,创建一个项目(示例是创建一个根项目的子项目),在项目级的 build.gradle
文件中添加插件声明和Gradle API依赖声明,如下示例所示:
plugins {id 'groovy' // Groovy 基于语言开发
}sourceSets {main {java.srcDirs = ["src/main/groovy"]}
}dependencies {// 引入Gradle Api 依赖implementation gradleApi()
}
需要注意的是,项目 build.gradle
文件中引入的插件,需要根据使用的开发语言添加,上面示例是基于groovy语言,因此引入 groovy 插件
(id 'groovy'
),如果使用 Java 语言,需要添加 java
插件(一般 JVM 项目已经默认引入,id 'java'
),如果使用 Kotlin 语言,需要引如 kotlin jvm 插件(id 'org.jetbrains.kotlin.jvm'
)。如果源码不是放在 Gradle 默认的源码目录,还需要指定源码目录(使用 sourceSets
配置)。基于groovy
语言,默认 projectDir/src/main/groovy
目录;基于 java
语言,默认 projectDir/src/main/java
目录;基于 kotlin
语言,默认 projectDir/src/main/kotlin
目录。
注意事项:如果插件配置不正确或者源码目录不正确,将会导致源码不被编译,生成的目标 jar 将不会包含 Task 类。
然后,将上面示例的代码移到独立项目中,放到 projectDir/src/main/groovy
目录下
src/main/groovy/com/example/CheckingTask.grooovy
import org.gradle.api.DefaultTask
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskActionabstract class CheckingTask extends DefaultTask {@Inputabstract Property<String> getOwner() // 添加一个owner属性GCheckingTask() {// 构造函数中设置默认值owner.convention("Unknown")}@TaskActiondef checkDisk() {println "CheckingTask -- checking disk -- ${owner.get()}"}
}
接下来,我们需要做的就是将项目的编译目标 jar 发布到仓库,这样就可以在其他项目中使用依赖引用。发布远程仓库的方法可以参考帖子 使用Gradle发布工件到Maven仓库(新版)。本示例中为了演示,就采用发布到本地仓库的形式:
- 在独立项目中的
build.gradle
添加maven-publish
插件
plugins {id 'groovy'id 'maven-publish' // Gradle 发布仓库插件
}
- 在独立项目中的
build.gradle
添加工件发布配置
publishing {publications {release(MavenPublication) {groupId "com.owen.test" // groupIdartifactId "ktcctask" // artifactIdversion "1.6" // 发布版本artifact "${project.buildDir}/libs/lib.jar" // 发布jar,执行发布任务时,确保发布的jar生成成功description "test task" // 说明描述}}
}
注意事项:发布工件,执行发布任务时请确保你发布的工件(jar)已经生成或者更新到最新,上面的示例中,需要每次都执行一次构建,才能完成编译更新。在下面,我们将介绍一个直接通过自定义任务的方式,执行一个任务就完成编译后发布的操作。
- 自定义任务,完成编译后发布
tasks.register("publishReleaseToLocal") {dependsOn assemble // 依赖 assemble,在前置执行 assemble 任务,先生成最新的jar
}publishReleaseToLocal.configure {// 配置 publishReleaseToLocal 任务,以 maven-publish 创建的发布任务 publishReleasePublicationToMavenLocal 结束,// 表示最终要执行 publishReleasePublicationToMavenLocal 任务,将工件发布到仓库finalizedBy(publishReleasePublicationToMavenLocal)
}
- 执行
publishReleaseToLocal
任务
gradle publishReleaseToLocal -q
完成以上的操作后,包含自定义 Task 类的工件就发布到了仓库(本地仓库在 用户目录/.m2
目录下可以找到工件内容),接下来,就是完成在其他项目中依赖引用自定义的 Task 类。在引用的项目的根项目级别的 build.gradle
文件中,添加插件依赖声明,如下示例:
buildscript {ext.kotlin_version = "1.4.0"repositories {google()mavenCentral()mavenLocal() // 本地仓库,如果是远程仓库私服,需配置私服}dependencies {classpath "com.android.tools.build:gradle:4.0.1"classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle filesclasspath "com.owen.test:ktcctask:1.6" // 配置自定义 Task 类仓库依赖}}
完成仓库配置和依赖配置,就可以在项目中引用自定义的 Task 类。
tasks.register('doChecking', CheckingTask) {owner = "Mr Zhang"doFirst {println "Start to check you computer"}doLast {println "Check computer finished"}
}
gradle doChecking -q
执行任务,输出:
Start to check you computer
CheckingTask -- checking disk -- Mr Zhang
Check computer finished
四、写在最后
其实自定义任务类还是比较简单,但是也需要注意一些细节,比如任务类必须是非 final 的(可继承)。学会基本的自定义任务类,为后续编写更加复杂的任务,甚至是编写插件打下基础。本编到此结束,希望能帮到各位读者。