Gradle任务入门

自定义Gradle tasks可以显著提高一个开发者的日常生活。任务可以操作存在的构建过程,添加新的构建步骤,或影响构建输出。你可以运行简单的任务,例如通过hooking到Gradle的Android插件,给一个生成的APK重命名。任务也能更加复杂的代码,你就可以在构建过程任何细节上做修改。当你了解如何hook到Android插件之后,更是如此。

定义任务

任务属于一个Project对象,并且每个任务都可以执行task接口。定义一个新任务的最简单的方式是,执行将任务名称作为其参数的任务方法:

task hello

其创建了任务,但当你执行时,它不会做任何事情。为了常见一个有用的任务,你需要添加一些动作,初学者通常会犯的一个错误像下面这样创建任务:

task hello{
println ‘hello,world!’
}

当你执行该任务时,会看到如下输出

hello,world!

从输出来看,你可能觉得任务运行了,但实际上,hello,world!在执行该任务之前就已被打印出来了。为了理解发生了什么,我们需要回到最初,我们知道在任意Gradle构建中,都有三个阶段:初始化阶段,配置阶段和执行阶段。当像上个例子那样以相同的方式添加代码到一个任务时,你实际上设置了任务的配置。即使你执行了不同的任务,hello,world!也依然会出现。

如果你想在执行阶段给一个任务添加动作,则可以使用下面的表示法:

扫描二维码关注公众号,回复: 11152167 查看本文章

task hello << {
println ‘hello,world!’
}

唯一的不同就是closure之前的<<,其告知Gradle,代码在执行阶段执行,而不是在配置阶段。

Groovy有很多简写,在Gradle中定义任务的常用方式有以下几种:

task(hello) << {
println ‘hello,world!’
}

task(‘hello’) << {
println ‘hello,world!’
}

tasks.create(name:‘hello’) << {
println ‘hello,world!’
}

前两个代码块只是以两种不同的方式通过Groovy来实现相同的事情。你可以使用括号,但你不需要这么做。你也不需要给参数加上单引号。在这两个代码块中,我们可以调用task()方法,其需要两个参数:一个是名为任务的字符串,另一个是closure。task()方法是Gradle Project类的一部分。

最后一个代码块没有使用task()方法。相反用了一个名叫tasks的对象。tasks对象是TaskContainer的实例,存在于每个Project对象中。该类提供了一个create()方法,需要一个Map和一个closure作为参数,最终返回一个Task。

任务剖析

Task接口是所有任务的基础,其定义了一系列属性和方法。所有这些都是由一个叫做DefaultTask的类实现的。这个标准的任务实现方式,你创建的每个新的任务,都是基于DefaultTask的。

每个任务都包含一个Action对象的集合。当一个任务被执行时,所有这些动作会以连续顺序被执行。你可以使用doFirst()和doLast()方法来为一个任务添加动作。这些方法都是以一个closure作为参数,然后被包装到一个Action对象中的。

如果你想在执行阶段执行你的代码,那么你需要使用doFirst()或doLast()来为一个task添加代码。我们之前用来定义tasks的左位移运算符(<<),就是doFirst()方法的简写。

下面使用doFirsh和doLast来写:

task hello{
println “Configuration”

doLast{
    println "doLast"
}

doFirst{
    println "doFirst"
}

}
执行结果
在这里插入图片描述

即使打印“doLast”的代码在打印“daFirst”的代码之前定义,但当执行task时,他们仍然会按正确的顺序执行。你甚至可以多次调用doFirst和doLast方法,如下所示:

task hello{
    doFirst{
        println "Not really first."
    }

    doFirst{
        println "First"
    }

    doLast{
        println "Not really last."
    }

    doLast{
        println "Last."
    }

}

执行这个task的输出如下:
在这里插入图片描述

注意,doFirst总是添加一个动作到task的最前面,而doLast总是添加一个动作到最后面。这意味着,当你使用这些方法时,需要小心,特别是当顺序十分重要的时候。

当涉及到tasks排序时,你可以使用mustRunAfter()方法。该方法将影响Gradle如何构建依赖关系图。当使用mustRunAfter()时,你需要指定,如果两个任务都被执行,那么必须有一个任务始终先执行。

task task1 << {
    println 'task1'
}

task task2 << {
    println 'task2'
}
task2.mustRunAfter task1

gradlew task2 task1
在这里插入图片描述

在这两个任务之间,mustRunAfter()方法不会添加任何依赖,因而我们可以只执行task2而不执行task1.如果你需要一个任务依赖另一个,那么可使用dependsOn()方法。

使用任务来简化release过程

在发布一个Android应用到应用商店之前,你需要使用证书对其签名。要想做到这一点,你需要创建自己的keystore,其中包含一对私钥。当你有了自己的keystore和私钥后,你就可以在FGradle中按照如下方式定义配置了:

signingConfigs {
        release{
            storeFile file("release.keystore")
            storePassword "password"
            keyAlias "ReleaseKey"
            keyPassword "password"
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }

这种方法的缺点是,你的keystore密码是以铭文的形式存放在依赖库中的,如果你正在为一个开源项目工作,那么这是明确禁止的。这会使得任意同时访问keystore文件和密码的人,都能使用你的身份发布应用程序。为了防止这种情况,你可以创建一个任务,在你每次装配发布包的时候,都询问发布密码。这样做有点麻烦,而且使得你不可能构建服务器来自动生成发布版本。有一个好的解决方案是创建一个配置文件,用来存放keystor密码,而不是将其包含到依赖仓库中。

在项目的根目录创建一个名为private。properties的文件夹,然后添加一行代码:

release.password = thepassword

我们假设keystore和密码本身相同。如果你有两个不同的密码,那么可以添加第二属性,这很容易。一旦设置完毕,你就可以定义一个名为getReleasePassword的新任务了:

task getReleasePassword << {
    def password = ''
    if (rootProject.file('private.properties').exists()){
        Properties properties = new Properties();
        properties.load(rootProject.file('private.properties').newDataInputStream())
        password = properties.getProperty('release.password')
    }
}

该任务会在项目的根目录搜索一个叫做private.properties的文件。如果该文件存在,则任务将加载其内容的所有属性。properties.load()方法梭巡key-value键值对,就像我们在属性文件中定义的release.password那样。

为了确保任何人在没有私有属性文件的情况下仍可运行脚本。可添加一个密码属性作为备用,以便运行脚本。

现在,我们的任务已经完成了,我们需要确保当执行release构建时,该任务被执行。要想做到这一点,需要在build.gradle文件中添加如下代码:

tasks.whenTaskAdded {
    theTask ->
    if (theTask.name.equals("packageRelease")) {
        theTask.dependsOn "getReleasePassword"
    }
}

这段代码通过添加一个closure来链接(hooks into)到Gradle和Android插件,其会在任务被添加到依赖关系图时运行。在packageRelease任务执行之前,不需要密码,所有我们可以确保packageRelease依赖于我们的getReleasePassword任务。我们不能只使用packageRealease.dependsOn()的原因时,Gradle的Android插件是基于构建variants动态生成的packaging任务,这意味着在Android插件发现所有构建variant之前,packageRelease任务都不会存在,即发现过程是在每个单独构建之前。

为了使用这个任务正常工作,hook到Gradle和Android插件是很有必要的。这是一个强大的概念。

原创文章 63 获赞 59 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u013049016/article/details/93613492