Gradle 使用教程之 Task 详解
最近打算学习下 gradle 在 Android 中的使用,结果百度出来的文章都是介绍性文章,没啥干货。后来找到 gradle 官网教程,自己对着撸。
Gradle 概述:
Gradle 是一个基于 Apache Ant 和 Apache Maven 概念的项目自动化构建工具。它使用一种基于 Groovy 的特定领域语言来声明项目设置,而不是传统的 XML。Gradle 就是工程的管理,帮我们做了依赖、打包、部署、发布、各种渠道的差异管理等工作。
Gradle优势:
一款最新的,功能最强大的构建工具,用它逼格更高
使用程序代替传统的XML配置,项目构建更灵活
丰富的第三方插件,让你随心所欲使用
Maven、Ant能做的,Gradle都能做,但是Gradle能做的,Maven、Ant不一定能做。
Groovy 是一种基于JVM的敏捷开发语言,结合了Python、Ruby和Smalltalk的许多强大的特性。Groovy可以与Java完美结合,而且可以使用Java所有的库,在语法上支持动态类型、闭包等新一代语言特性,无缝集成所有已经存在的Java类库,既支持面向对象编程也支持面向过程编程
Groovy 优势:
- 一种更加敏捷的编程语言
- 入门非常的容易,且功能非常的强大
- 既可以作为编程语言也可以作为脚本语言
刚开始的时候,我对 Gradle 和 Groovy 傻傻分不清楚,以为都是一种语言。后来才懂了,gradle 是一个构建工具,使用的语言是 Groovy。
准备工作:
下面进入实战。
首先为了使用 gradle,大家可以在 Android studio 新建一个 Android 工程。使用其他 IDE 或者需要配置的环境的朋友,可以自己百度相关文章。
文章示例基于 Gradle 5.1.1 构建的。
Task
Gradle 中的所有内容都基于两个基本概念:项目和任务。
每个 Gradle 构建都由一个或多个项目组成。项目代表什么取决于您在 Gradle 中所做的事情。例如,一个项目可能代表一个 JAR 库或一个 Web 应用程序。它可能表示从其他项目产生的 JAR 组装而成的发行版 ZIP。项目不一定代表要构建的事物。它可能表示要完成的事情,例如将应用程序部署到暂存或生产环境。暂时不要担心这似乎还不清楚。Gradle 的按惯例构建支持为项目的定义添加了更具体的定义。
每个项目由一个或多个任务组成。任务代表构建执行的一些原子工作。这可能是编译某些类,创建 JAR,生成 Javadoc 或将一些存档发布到存储库。
现在,将研究在一个项目中构建一些简单的任务。后面的章节将介绍处理多个项目,以及有关处理项目和任务的更多信息。
Hello world
同样,也是先从 hello world 入门。您可以使用以下 gradle
命令运行 Gradle 构建。该 gradle
命令在当前目录中查找名为 build.gradle
的文件。这个 build.gradle
文件称为构建脚本,尽管严格来说,它是一个构建配置脚本,我们将在后面看到。构建脚本定义项目及其任务。
要尝试此操作,请创建以下名为的构建脚本 build.gradle
。
// build.gradle
task hello {
doLast {
println 'Hello world!'
}
}
在项目的移至包含的目录并使用以下命令执行构建脚本:
./gradlew -q hello // Android 用户在根目录使用 ./gradlew
gradle -q hello // 非 Android 用户使用 gradle
使用 -q
命令行选项运行。这将取消 Gradle 的日志消息,因此仅显示任务的输出。这样可以使示例输出更加清晰。如果不想,则不需要使用此选项。
后面直接将执行构建脚本的命令放在注释前,不在单行作为展示了。
定义任务
在这里,你将看到了如何使用字符串作为任务名称来定义任务。此样式有一些变体,您可能需要在某些情况下使用。
task('hello') {
doLast {
println "hello"
}
} task('copy', type: Copy) {
from(file('srcDir'))
into(buildDir)
}
上面创建了两个任务分别是 hello 和 copy。有一种定义任务的替代语法,您可能更喜欢使用:
tasks.create('hello') {
doLast {
println "hello"
}
} tasks.create('copy', Copy) {
from(file('srcDir'))
into(buildDir)
}
上面同样创建了两个任务分别是 hello 和 copy。 最后,Groovy 和 Kotlin DSL 有特定于语言的语法:
// Using Groovy dynamic keywords task(hello) {
doLast {
println "hello"
}
} task(copy, type: Copy) {
from(file('srcDir'))
into(buildDir)
}
采用代码构建脚本
Gradle 的构建脚本为您提供了 Groovy 和 Kotlin 的全部功能。作为一个开胃菜,看看这个:在Gradle的任务中使用 Groovy 或 Kotlin :
//gradle -q upper
task upper {
doLast {
String someString = 'mY_nAmE'
println "Original: $someString"
println "Upper case: ${someString.toUpperCase()}"
}
}
// gradle -q count
task count {
doLast {
4.times { print "$it " }
}
}
任务之间的依赖
任务之间可以具有依赖性,关键字 dependsOn :
// gradle -q intro
task hello {
doLast {
println 'Hello world!'
}
}
task intro {
dependsOn hello
doLast {
println "I'm Gradle"
}
}
上面的依赖是依赖的任务先声明,然后再进行依赖,还有一种依赖是懒惰性依赖,被依赖的任务可以后面再声明,但是如果不声明的会报错:
// gradle -q taskX
task taskX {
dependsOn 'taskY'
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
任务 taskX 依赖的任务 taskY 是后声明的。
您可以通过多种方式定义任务的依赖关系。在“ 任务依赖项”中,介绍了使用任务名称定义依赖项。任务名称可以引用与该任务在同一项目中的任务,也可以引用其他项目中的任务。要引用另一个项目中的任务,请在任务名称前添加其所属项目的路径。以下是添加从 projectA:taskX
到的依赖的示例 projectB:taskY
:
// gradle -q taskX
project('projectA') {
task taskX {
dependsOn ':projectB:taskY'
doLast {
println 'taskX'
}
}
} project('projectB') {
task taskY {
doLast {
println 'taskY'
}
}
}
此处的 projectA,projectB 要改成你项目中的名字,简单来说,就是不同层级的任务也是可以相互依赖的。
动态任务
Groovy 或 Kotlin 的功能可用于定义任务以外的其他功能。例如,您也可以使用它来动态创建任务。
// gradle -q task1
4.times { counter ->
task "task$counter" {
doLast {
println "I'm task number $counter"
}
}
}
上述创建了 4 个 task,分别是 task0,task1,task2,task3。
操作已创建的任务
任务创建后,就可以通过 API 对其进行访问。例如,您可以在运行时为任务动态添加依赖项。
// gradle -q task0
4.times { counter ->
task "task$counter" {
doLast {
println "I'm task number $counter"
}
}
}
task0.dependsOn task2, task3
或者,您可以将行为添加到现有任务。
// gradle -q hello
task hello {
doLast {
println 'Hello Earth'
}
}
hello.doFirst {
println 'Hello Venus'
}
hello.configure {
doLast {
println 'Hello Mars'
}
}
hello.configure {
doLast {
println 'Hello Jupiter'
}
}
调用 doFirst
和 doLast
可以执行多次。他们将操作添加到任务操作列表的开头或结尾。执行任务时,将按顺序执行操作列表中的操作。
Groovy DSL快捷方式符号
访问现有任务有一种方便的表示法。每个任务都可以作为构建脚本的属性来使用:
// gradle -q hello
task hello {
doLast {
println 'Hello world!'
}
}
hello.doLast {
println "Greetings from the $hello.name task."
}
例子中,通过获取任务的名字可以知道这个是来自于 task hello 的任务所做的事。这样可以提高代码的可读性,尤其是在使用插件提供的任务(例如compile
任务)时。
额外任务属性
您可以将自己的属性添加到任务上。要添加名为的属性 myProperty
,并为 ext.myProperty 设置
初始值。就可以像预定义的任务属性一样读取和设置属性。
// gradle -q printTaskProperties
task myTask {
ext.myProperty = "myValue"
} task printTaskProperties {
doLast {
println myTask.myProperty
}
}
默认任务
如果未指定其他任务,则Gradle允许您定义一个或多个默认任务。
// gradle -q
defaultTasks 'clean', 'run' task clean {
doLast {
println 'Default Cleaning!'
}
} task run {
doLast {
println 'Default Running!'
}
} task other {
doLast {
println "I'm not a default task!"
}
}
这等效于运行 gradle clean run
。在多项目构建中,每个子项目都可以有其自己的特定默认任务。如果子项目未指定默认任务,则使用父项目的默认任务(如果已定义)
通过DAG进行配置
正如我们稍后将详细描述的(请参阅 Build Lifecycle),Gradle具有配置阶段和执行阶段。在配置阶段之后,Gradle 知道应该执行的所有任务。Gradle 为您提供了一个利用此信息的机会。一个用例是检查发布任务是否在要执行的任务中。以此为基础,您可以为某些变量分配不同的值。
在以下示例中,distribution
和 release
任务的执行导致 version
变量的值不同。
// gradle -q distribution
// gradle -q release
task distribution {
doLast {
println "We build the zip with version=$version"
}
} task release {
dependsOn 'distribution'
doLast {
println 'We release now'
}
} gradle.taskGraph.whenReady { taskGraph ->
if (taskGraph.hasTask(":release")) {
version = '1.0'
} else {
version = '1.0-SNAPSHOT'
}
}
可以发现,此处,执行不同的 task 具有不同结果。
构建脚本的外部依赖关系
如果构建脚本需要使用外部库,则可以将它们添加到构建脚本本身中的脚本的类路径中。您可以使用 buildscript()
方法执行此操作,并传入一个声明构建脚本类路径的块。
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
}
buildscript()
方法中的代码块将构成 ScriptHandler 实例。您可以通过向 classpath
配置添加依赖项来声明构建脚本类路径。这与您声明 Java 编译类路径的方式相同。您可以使用除项目依赖项以外的任何 依赖项类型。
声明了构建脚本类路径后,就可以像在该类路径上的任何其他类一样使用构建脚本中的类。以下示例将添加到前面的示例中,并使用构建脚本类路径中的类。
//gradle -q encode
import org.apache.commons.codec.binary.Base64 buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
} task encode {
doLast {
def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
println new String(encodedString)
}
}
这里先是添加了依赖项,然后再创建了一个任务,引用了依赖项中的类来实现对字符串的加密。
访问任务的属性
您通常需要找到在构建文件中定义的任务,例如,对其进行配置或将其用于依赖项。有很多方法可以做到这一点。首先,就像定义任务一样,Groovy和Kotlin DSL具有特定于语言的语法
task hello
task copy(type: Copy) // Access tasks using Groovy dynamic properties on Project println hello.name
println project.hello.name println copy.destinationDir
println project.copy.destinationDir
任务也可以通过 tasks
集合获得。
task hello
task copy(type: Copy) println tasks.hello.name
println tasks.named('hello').get().name println tasks.copy.destinationDir
println tasks.named('copy').get().destinationDir
您可以使用 tasks.getByPath()
方法使用任务的路径从任何项目访问任务。您可以 getByPath()
使用任务名称,相对路径或绝对路径来调用该方法。
project(':projectA') {
task hello
} task hello println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
println tasks.getByPath('projectA:hello').path
println tasks.getByPath(':projectA:hello').path
将参数传递给任务构造函数
与 Task
在创建后配置变量的可变属性相反,您可以将参数值传递给 Task
类的构造函数。为了将值传递给 Task
构造函数,您必须使用注释相关的构造函数 @javax.inject.Inject
。
class CustomTask extends DefaultTask {
final String message
final int number @Inject
CustomTask(String message, int number) {
this.message = message
this.number = number
}
}
然后,您可以创建一个任务,并在参数列表的末尾传递构造函数参数。
tasks.create('myTask', CustomTask, 'hello', 42)
task myTask(type: CustomTask, constructorArgs: ['hello', 42])
上述两种方法都可以。在所有情况下,作为构造函数参数传递的值都必须为非 null。如果您尝试传递一个 null
值,Gradle 将抛出一个 NullPointerException
指示,指出哪个运行时值是 null
。
向任务添加描述
您可以在任务中添加描述。执行 gradle tasks 时将显示此描述。
// gradle tasks
task copy(type: Copy) {
description 'Copies the resource directory to the target directory.'
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}
更换任务
有时您想替换任务。例如,如果要将 Java 插件添加的任务与其他类型的自定义任务交换。您可以使用以下方法实现此目的:
// gradle -q copy
task copy(type: Copy) task copy(overwrite: true) {
doLast {
println('I am the new one.')
}
}
定义新任务时,必须将 overwrite
属性设置为 true。否则,Gradle 会引发异常,说该名称的任务已经存在。
跳过任务
Gradle 提供了多种方法来跳过任务的执行。
使用谓词
您可以使用该 onlyIf()
方法将谓词附加到任务。仅当谓词评估为 true 时,才执行任务的动作。您将谓词实现为闭包。闭包作为参数传递给任务,如果任务应执行,则应返回 true;如果应跳过任务,则应返回 false。在即将执行任务之前就对谓词进行评估。
//gradle hello -PskipHello
task hello {
doLast {
println 'hello world'
}
} hello.onlyIf { !project.hasProperty('skipHello') }
使用StopExecutionException
如果不能用谓词来表示跳过任务的逻辑,则可以使用 StopExecutionException。如果某个动作引发了此异常,则将跳过该动作的进一步执行以及该任务的任何后续动作的执行。构建继续执行下一个任务。
// gradle -q myTask
task compile {
doLast {
println 'We are doing the compile.'
}
} compile.doFirst {
// Here you would put arbitrary conditions in real life.
// But this is used in an integration test so we want defined behavior.
if (true) { throw new StopExecutionException() }
}
task myTask {
dependsOn('compile')
doLast {
println 'I am not affected'
}
}
启用和禁用任务
每个任务都有一个 enabled
默认为的标志 true
。将其设置为 false
阻止执行任何任务动作。禁用的任务将标记为“跳过”。
task disableMe {
doLast {
println 'This should not be printed if the task is disabled.'
}
}
disableMe.enabled = false
任务超时
每个任务都有一个 timeout
可用于限制其执行时间的属性。当任务达到超时时,其任务执行线程将被中断。该任务将被标记为失败。终结器任务仍将运行。如果 --continue
使用,其他任务可以在此之后继续运行。不响应中断的任务无法超时。Gradle 的所有内置任务均会及时响应超时
task hangingTask() {
doLast {
Thread.sleep(100000)
}
timeout = Duration.ofMillis(500)
}
任务规则
有时您想执行一个任务,该任务的行为取决于较大或无限数量的参数值范围。提供此类任务的一种非常好的表达方式是任务规则:
// gradle -q pingServer1
tasks.addRule("Pattern: ping<ID>") { String taskName ->
if (taskName.startsWith("ping")) {
task(taskName) {
doLast {
println "Pinging: " + (taskName - 'ping')
}
}
}
}
规则不仅在从命令行调用任务时使用。您还可以在基于规则的任务上创建 dependsOn 关系:
// gradle -qgroupPing
tasks.addRule("Pattern: ping<ID>") { String taskName ->
if (taskName.startsWith("ping")) {
task(taskName) {
doLast {
println "Pinging: " + (taskName - 'ping')
}
}
}
} task groupPing {
dependsOn pingServer1, pingServer2
}
如果运行“ gradle -q tasks
”,将找不到名为“ pingServer1
”或“ pingServer2
” 的任务,但是此脚本正在根据运行这些任务的请求执行逻辑。
Finalizer tasks
当计划运行终结任务时,Finalizer tasks 会自动添加到任务图中。
//gradle -q taskX
task taskX {
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
} taskX.finalizedBy taskY
即使终结任务失败,也将执行 Finalizer tasks。
// gradle -q taskX
task taskX {
doLast {
println 'taskX'
throw new RuntimeException()
}
}
task taskY {
doLast {
println 'taskY'
}
} taskX.finalizedBy taskY
运行结果:
Output of gradle -q taskX
> gradle -q taskX
taskX
taskY FAILURE: Build failed with an exception. * Where:
Build file '/home/user/gradle/samples/groovy/build.gradle' line: 4 * What went wrong:
Execution failed for task ':taskX'.
> java.lang.RuntimeException (no error message) * Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 0s
在构建创建无论构建失败还是成功都必须清除的资源的情况下,终结器任务很有用。这种资源的一个示例是一个Web容器,它在集成测试任务之前启动,并且即使某些测试失败,也应始终将其关闭。
要指定终结器任务,请使用 Task.finalizedBy(java.lang.Object ...) 方法。此方法接受Task实例,任务名称或 Task.dependsOn(java.lang.Object…) 接受的任何其他输入。
到此,关于 task 的讲解到这里就结束了。
Gradle 使用教程之 Task 详解的更多相关文章
- Vue 进阶教程之:详解 v-model
分享 Vue 官网教程上关于 v-model 的讲解不是十分的详细,写这篇文章的目的就是详细的剖析一下, 并介绍 Vue 2.2 v-model改进的地方,然后穿插的再说点 Vue 的小知识. 在 V ...
- Gradle技术之四 - Gradle的Task详解
1 Gradle的Task详解 1 Task定义和配置 2 Task的执行 3 Task的依赖和执行顺序 4 Task类型 5 Task结合gradle的生命周期 6 Task实战 1.1 Task定 ...
- gradle中的build script详解
目录 简介 project和task 一个例子 task详细讲解 task脚本 task依赖 动态task 默认task build script的外部依赖 gradle中的build script详 ...
- 高并发网络编程之epoll详解(转载)
高并发网络编程之epoll详解(转载) 转载自:https://blog.csdn.net/shenya1314/article/details/73691088 在linux 没有实现epoll事件 ...
- Linux应用编程之lseek详解
Linux应用编程之lseek详解 1.lseek函数介绍 (1).文件指针:当我们要对一个文件进行读写时,一定要先打开这个文件,所以我们读写的所有文件都是动态文件.动态文件在内存中的形态就是文件流的 ...
- Android Gradle 自定义Task 详解
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/76408024 本文出自[赵彦军的博客] 一:Gradle 是什么 Gradle是一 ...
- iOS多线程之NSOperation详解
使用NSOperation和NSOperationQueue进行多线程开发,只要将一个NSOperation(实际开发中需要使用其子类 NSInvocationOperation,NSBlockOpe ...
- Activity的Task详解
1.Task Task是一个具有栈结构(后进先出)的容器,可以放置多个Activity实例.启动一个应用,系统就会为之创建一个Task,来放置根Activity.默认情况下,一个Activity启动另 ...
- asyncio异步IO--协程(Coroutine)与任务(Task)详解
摘要:本文翻译自Coroutines and Tasks,主要介绍asyncio中用于处理协程和任务的方法和接口.在翻译过程中,译者在官方文档的基础上增加了部分样例代码和示意图表,以帮助读者对文档的理 ...
随机推荐
- 【Layui】侧边菜单导航禁用折叠、去除箭头样式
官方提供的样式代码: <ul class="layui-nav layui-nav-tree" lay-filter="test"> <!-- ...
- JavaScript 图片与Base64数据互相转换脚本
JavaScript 图片与Base64数据互相转换脚本 注: 转换过程中注意跨域问题.测试页是否支持相关标签创建.dom结构. 方法一:非Html 5使用FileReader 使用XMLHttpRe ...
- 华硕主板 Vmware虚拟机 二进制转换与此平台上的长模式不兼容
出现情况如下: 大概遇到过两次这个问题,第一次是在笔记本VM上装虚拟机,第二次是在台式机VM上装虚拟机. 原因是因为虚拟化(Intel Virtualization Technology)技术,在主板 ...
- 【LeetCode算法-53】Maximum Subarray
Given an integer array nums, find the contiguous subarray (containing at least one number) which has ...
- EasyDSS高性能RTMP、HLS(m3u8)、HTTP-FLV、RTSP流媒体服务器与EasyDSS流媒体解决方案的不同
背景分析 众所周知,立足于视频软件的开发,我们的产品很多.经常有客户问到我们产品的差别以及某个产品在某个系统架构中的具体定位,因此我想通过一系列的博客说明一下,不同产品之间的区别,以及在具体架构中自身 ...
- Postgresql单表【插入】/【更新】百万数据
一.插入数据 说到插入数据,一开始就想到: insert int A values(*******************) 插入多条数据,最多想到:写成这样: insert into A value ...
- php提供一维数组模糊查询
2019年9月30日14:36:15 提供一维数组模糊查询,只支持utf-8 内部处理是Unicode 编码特殊编码格式的可能会出错 if (!function_exists('arrayFuzzyQ ...
- url、href、src
一.URL的概念 统一资源定位符(或称统一资源定位器/定位地址.URL地址等,英语:Uniform Resource Locator,常缩写为URL),有时也被俗称为网页地址(网址).如同在网络上的门 ...
- C#.NET 合并图片
引用:https://www.cnblogs.com/stulzq/p/6137715.html util: using System; using System.Collections.Generi ...
- 学习 Git Rebase
有问题为什么不问问神奇的 man 呢? rebase 也算是我比较常用的一个指令了,但是很长时间以来,对这个指令的认识还是不够深刻,于是就找了个时间认真地读了一下 git rebase 的文档.这份文 ...