Gradle入门篇之自定义Task类

一、前言

    在编写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对象的时候,通过 doFirstdoLast 闭包,向任务中添加行为动作,如下示例:

abstract class CheckingTask extends org.gradle.api.DefaultTask {
    @org.gradle.api.tasks.TaskAction
    def 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.Input
    abstract org.gradle.api.provider.Property<String> getOwner() // 添加一个owner属性

    CheckingTask() {
        // 构造函数中设置默认值
        owner.convention("Unknown")
    }

    @org.gradle.api.tasks.TaskAction
    def 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.TaskAction

abstract class CheckingTask extends DefaultTask {

    @Input
    abstract Property<String> getOwner() // 添加一个owner属性

    GCheckingTask() {
        // 构造函数中设置默认值
        owner.convention("Unknown")
    }

    @TaskAction
    def 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" // groupId
            artifactId "ktcctask" // artifactId
            version "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 files

        classpath "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 的(可继承)。学会基本的自定义任务类,为后续编写更加复杂的任务,甚至是编写插件打下基础。本编到此结束,希望能帮到各位读者。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值