Gradle 使用教程之 Task 详解
最近打算學(xué)習(xí)下 gradle 在 Android 中的使用,結(jié)果百度出來的文章都是介紹性文章,沒啥干貨。后來找到 gradle 官網(wǎng)教程,自己對著擼。
Gradle 概述:
Gradle 是一個(gè)基于 Apache Ant 和 Apache Maven 概念的項(xiàng)目自動化構(gòu)建工具。它使用一種基于 Groovy 的特定領(lǐng)域語言來聲明項(xiàng)目設(shè)置,而不是傳統(tǒng)的 XML。Gradle 就是工程的管理,幫我們做了依賴、打包、部署、發(fā)布、各種渠道的差異管理等工作。
Gradle優(yōu)勢:
一款最新的,功能最強(qiáng)大的構(gòu)建工具,用它逼格更高
使用程序代替?zhèn)鹘y(tǒng)的XML配置,項(xiàng)目構(gòu)建更靈活
豐富的第三方插件,讓你隨心所欲使用
Maven、Ant能做的,Gradle都能做,但是Gradle能做的,Maven、Ant不一定能做。
Groovy 是一種基于JVM的敏捷開發(fā)語言,結(jié)合了Python、Ruby和Smalltalk的許多強(qiáng)大的特性。Groovy可以與Java完美結(jié)合,而且可以使用Java所有的庫,在語法上支持動態(tài)類型、閉包等新一代語言特性,無縫集成所有已經(jīng)存在的Java類庫,既支持面向?qū)ο缶幊桃仓С置嫦蜻^程編程
Groovy 優(yōu)勢:
一種更加敏捷的編程語言
入門非常的容易,且功能非常的強(qiáng)大
既可以作為編程語言也可以作為腳本語言
剛開始的時(shí)候,我對 Gradle 和 Groovy 傻傻分不清楚,以為都是一種語言。后來才懂了,gradle 是一個(gè)構(gòu)建工具,使用的語言是Groovy。
準(zhǔn)備工作:
下面進(jìn)入實(shí)戰(zhàn)。
首先為了使用 gradle,大家可以在 Android studio 新建一個(gè) Android 工程。使用其他 IDE 或者需要配置的環(huán)境的朋友,可以自己百度相關(guān)文章。
文章示例基于 Gradle 5.1.1 構(gòu)建的。
Task
Gradle 中的所有內(nèi)容都基于兩個(gè)基本概念:項(xiàng)目和任務(wù)。
每個(gè) Gradle 構(gòu)建都由一個(gè)或多個(gè)項(xiàng)目組成。項(xiàng)目代表什么取決于您在 Gradle 中所做的事情。例如,一個(gè)項(xiàng)目可能代表一個(gè) JAR 庫或一個(gè) Web 應(yīng)用程序。它可能表示從其他項(xiàng)目產(chǎn)生的 JAR 組裝而成的發(fā)行版 ZIP。項(xiàng)目不一定代表要構(gòu)建的事物。它可能表示要完成的事情,例如將應(yīng)用程序部署到暫存或生產(chǎn)環(huán)境。暫時(shí)不要擔(dān)心這似乎還不清楚。Gradle 的按慣例構(gòu)建支持為項(xiàng)目的定義添加了更具體的定義。
每個(gè)項(xiàng)目由一個(gè)或多個(gè)任務(wù)組成。任務(wù)代表構(gòu)建執(zhí)行的一些原子工作。這可能是編譯某些類,創(chuàng)建 JAR,生成 Javadoc 或?qū)⒁恍┐鏅n發(fā)布到存儲庫。
現(xiàn)在,將研究在一個(gè)項(xiàng)目中構(gòu)建一些簡單的任務(wù)。后面的章節(jié)將介紹處理多個(gè)項(xiàng)目,以及有關(guān)處理項(xiàng)目和任務(wù)的更多信息。
Hello world
同樣,也是先從 hello world 入門。您可以使用以下gradle命令運(yùn)行 Gradle 構(gòu)建。該gradle命令在當(dāng)前目錄中查找名為build.gradle的文件。這個(gè)build.gradle文件稱為構(gòu)建腳本,盡管嚴(yán)格來說,它是一個(gè)構(gòu)建配置腳本,我們將在后面看到。構(gòu)建腳本定義項(xiàng)目及其任務(wù)。
要嘗試此操作,請創(chuàng)建以下名為的構(gòu)建腳本build.gradle。
例子1. 第一個(gè)構(gòu)建腳本,在 android 項(xiàng)目中找到 build.gradle,在里面添加以下代碼:
// build.gradle
task hello {
doLast {
println 'Hello world!'
}
}
在項(xiàng)目的移至包含的目錄并使用以下命令執(zhí)行構(gòu)建腳本:
./gradlew -q hello // Android 用戶在根目錄使用 ./gradlew
gradle -q hello // 非 Android 用戶使用 gradle
使用-q命令行選項(xiàng)運(yùn)行。這將取消 Gradle 的日志消息,因此僅顯示任務(wù)的輸出。這樣可以使示例輸出更加清晰。如果不想,則不需要使用此選項(xiàng)。
后面直接將執(zhí)行構(gòu)建腳本的命令放在注釋前,不在單行作為展示了。
定義任務(wù)
在這里,你將看到了如何使用字符串作為任務(wù)名稱來定義任務(wù)。此樣式有一些變體,您可能需要在某些情況下使用。
task('hello') {
doLast {
println "hello"
}
}
task('copy', type: Copy) {
from(file('srcDir'))
into(buildDir)
}
上面創(chuàng)建了兩個(gè)任務(wù)分別是 hello 和 copy。有一種定義任務(wù)的替代語法,您可能更喜歡使用:
tasks.create('hello') {
doLast {
println "hello"
}
}
tasks.create('copy', Copy) {
from(file('srcDir'))
into(buildDir)
}
上面同樣創(chuàng)建了兩個(gè)任務(wù)分別是 hello 和 copy。最后,Groovy 和 Kotlin DSL 有特定于語言的語法:
// Using Groovy dynamic keywords
task(hello) {
doLast {
println "hello"
}
}
task(copy, type: Copy) {
from(file('srcDir'))
into(buildDir)
}
采用代碼構(gòu)建腳本
Gradle 的構(gòu)建腳本為您提供了 Groovy 和 Kotlin 的全部功能。作為一個(gè)開胃菜,看看這個(gè):在Gradle的任務(wù)中使用 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 " }
}
}
任務(wù)之間的依賴
任務(wù)之間可以具有依賴性,關(guān)鍵字dependsOn :
// gradle -q intro
task hello {
doLast {
println 'Hello world!'
}
}
task intro {
dependsOn hello
doLast {
println "I'm Gradle"
}
}
上面的依賴是依賴的任務(wù)先聲明,然后再進(jìn)行依賴,還有一種依賴是懶惰性依賴,被依賴的任務(wù)可以后面再聲明,但是如果不聲明的會報(bào)錯(cuò):
// gradle -q taskX
task taskX {
dependsOn 'taskY'
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
任務(wù)taskX 依賴的任務(wù)taskY 是后聲明的。
您可以通過多種方式定義任務(wù)的依賴關(guān)系。在“任務(wù)依賴項(xiàng)”中,介紹了使用任務(wù)名稱定義依賴項(xiàng)。任務(wù)名稱可以引用與該任務(wù)在同一項(xiàng)目中的任務(wù),也可以引用其他項(xiàng)目中的任務(wù)。要引用另一個(gè)項(xiàng)目中的任務(wù),請?jiān)谌蝿?wù)名稱前添加其所屬項(xiàng)目的路徑。以下是添加從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 要改成你項(xiàng)目中的名字,簡單來說,就是不同層級的任務(wù)也是可以相互依賴的。
動態(tài)任務(wù)
Groovy 或 Kotlin 的功能可用于定義任務(wù)以外的其他功能。例如,您也可以使用它來動態(tài)創(chuàng)建任務(wù)。
// gradle -q task1
4.times { counter ->
task "task$counter" {
doLast {
println "I'm task number $counter"
}
}
}
上述創(chuàng)建了 4 個(gè) task,分別是 task0,task1,task2,task3。
操作已創(chuàng)建的任務(wù)
任務(wù)創(chuàng)建后,就可以通過API對其進(jìn)行訪問。例如,您可以在運(yùn)行時(shí)為任務(wù)動態(tài)添加依賴項(xiàng)。
// gradle -q task0
4.times { counter ->
task "task$counter" {
doLast {
println "I'm task number $counter"
}
}
}
task0.dependsOn task2, task3
或者,您可以將行為添加到現(xiàn)有任務(wù)。
// 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' } }
調(diào)用doFirst和doLast可以執(zhí)行多次。他們將操作添加到任務(wù)操作列表的開頭或結(jié)尾。執(zhí)行任務(wù)時(shí),將按順序執(zhí)行操作列表中的操作。
Groovy DSL快捷方式符號
訪問現(xiàn)有任務(wù)有一種方便的表示法。每個(gè)任務(wù)都可以作為構(gòu)建腳本的屬性來使用:
// gradle -q hello
task hello {
doLast {
println 'Hello world!'
}
}
hello.doLast {
println "Greetings from the $hello.name task."
}
例子中,通過獲取任務(wù)的名字可以知道這個(gè)是來自于 task hello 的任務(wù)所做的事。這樣可以提高代碼的可讀性,尤其是在使用插件提供的任務(wù)(例如compile任務(wù))時(shí)。
額外任務(wù)屬性
您可以將自己的屬性添加到任務(wù)上。要添加名為的屬性myProperty,并為ext.myProperty 設(shè)置初始值。就可以像預(yù)定義的任務(wù)屬性一樣讀取和設(shè)置屬性。
// gradle -q printTaskProperties
task myTask {
ext.myProperty = "myValue"
}
task printTaskProperties {
doLast {
println myTask.myProperty
}
}
默認(rèn)任務(wù)
如果未指定其他任務(wù),則Gradle允許您定義一個(gè)或多個(gè)默認(rèn)任務(wù)。
// 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!"
}
}
這等效于運(yùn)行gradle clean run。在多項(xiàng)目構(gòu)建中,每個(gè)子項(xiàng)目都可以有其自己的特定默認(rèn)任務(wù)。如果子項(xiàng)目未指定默認(rèn)任務(wù),則使用父項(xiàng)目的默認(rèn)任務(wù)(如果已定義)
通過DAG進(jìn)行配置
正如我們稍后將詳細(xì)描述的(請參閱Build Lifecycle),Gradle具有配置階段和執(zhí)行階段。在配置階段之后,Gradle 知道應(yīng)該執(zhí)行的所有任務(wù)。Gradle 為您提供了一個(gè)利用此信息的機(jī)會。一個(gè)用例是檢查發(fā)布任務(wù)是否在要執(zhí)行的任務(wù)中。以此為基礎(chǔ),您可以為某些變量分配不同的值。
在以下示例中,distribution和release任務(wù)的執(zhí)行導(dǎo)致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'
}
}
可以發(fā)現(xiàn),此處,執(zhí)行不同的 task 具有不同結(jié)果。
構(gòu)建腳本的外部依賴關(guān)系
如果構(gòu)建腳本需要使用外部庫,則可以將它們添加到構(gòu)建腳本本身中的腳本的類路徑中。您可以使用buildscript()方法執(zhí)行此操作,并傳入一個(gè)聲明構(gòu)建腳本類路徑的塊。
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
}
buildscript()方法中的代碼塊將構(gòu)成ScriptHandler實(shí)例。您可以通過向classpath配置添加依賴項(xiàng)來聲明構(gòu)建腳本類路徑。這與您聲明 Java 編譯類路徑的方式相同。您可以使用除項(xiàng)目依賴項(xiàng)以外的任何依賴項(xiàng)類型。
聲明了構(gòu)建腳本類路徑后,就可以像在該類路徑上的任何其他類一樣使用構(gòu)建腳本中的類。以下示例將添加到前面的示例中,并使用構(gòu)建腳本類路徑中的類。
//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 '.getBytes()) println new String(encodedString) } }
這里先是添加了依賴項(xiàng),然后再創(chuàng)建了一個(gè)任務(wù),引用了依賴項(xiàng)中的類來實(shí)現(xiàn)對字符串的加密。
訪問任務(wù)的屬性
您通常需要找到在構(gòu)建文件中定義的任務(wù),例如,對其進(jìn)行配置或?qū)⑵溆糜谝蕾図?xiàng)。有很多方法可以做到這一點(diǎn)。首先,就像定義任務(wù)一樣,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
任務(wù)也可以通過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()方法使用任務(wù)的路徑從任何項(xiàng)目訪問任務(wù)。您可以getByPath()使用任務(wù)名稱,相對路徑或絕對路徑來調(diào)用該方法。
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
將參數(shù)傳遞給任務(wù)構(gòu)造函數(shù)
與Task在創(chuàng)建后配置變量的可變屬性相反,您可以將參數(shù)值傳遞給Task類的構(gòu)造函數(shù)。為了將值傳遞給Task構(gòu)造函數(shù),您必須使用注釋相關(guān)的構(gòu)造函數(shù)@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
}
}
然后,您可以創(chuàng)建一個(gè)任務(wù),并在參數(shù)列表的末尾傳遞構(gòu)造函數(shù)參數(shù)。
tasks.create('myTask', CustomTask, 'hello', 42)
task myTask(type: CustomTask, constructorArgs: ['hello', 42])
上述兩種方法都可以。在所有情況下,作為構(gòu)造函數(shù)參數(shù)傳遞的值都必須為非 null。如果您嘗試傳遞一個(gè)null值,Gradle 將拋出一個(gè)NullPointerException指示,指出哪個(gè)運(yùn)行時(shí)值是null。
向任務(wù)添加描述
您可以在任務(wù)中添加描述。執(zhí)行 gradle tasks 時(shí)將顯示此描述。
// gradle tasks
task copy(type: Copy) {
description 'Copies the resource directory to the target directory.'
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}
更換任務(wù)
有時(shí)您想替換任務(wù)。例如,如果要將 Java 插件添加的任務(wù)與其他類型的自定義任務(wù)交換。您可以使用以下方法實(shí)現(xiàn)此目的:
// gradle -q copy
task copy(type: Copy)
task copy(overwrite: true) {
doLast {
println('I am the new one.')
}
}
定義新任務(wù)時(shí),必須將overwrite屬性設(shè)置為 true。否則,Gradle 會引發(fā)異常,說該名稱的任務(wù)已經(jīng)存在。
跳過任務(wù)
Gradle 提供了多種方法來跳過任務(wù)的執(zhí)行。
使用謂詞
您可以使用該onlyIf()方法將謂詞附加到任務(wù)。僅當(dāng)謂詞評估為 true 時(shí),才執(zhí)行任務(wù)的動作。您將謂詞實(shí)現(xiàn)為閉包。閉包作為參數(shù)傳遞給任務(wù),如果任務(wù)應(yīng)執(zhí)行,則應(yīng)返回 true;如果應(yīng)跳過任務(wù),則應(yīng)返回 false。在即將執(zhí)行任務(wù)之前就對謂詞進(jìn)行評估。
//gradle hello -PskipHello
task hello { doLast { println 'hello world' } } hello.onlyIf { !project.hasProperty('skipHello') }
使用StopExecutionException
如果不能用謂詞來表示跳過任務(wù)的邏輯,則可以使用StopExecutionException。如果某個(gè)動作引發(fā)了此異常,則將跳過該動作的進(jìn)一步執(zhí)行以及該任務(wù)的任何后續(xù)動作的執(zhí)行。構(gòu)建繼續(xù)執(zhí)行下一個(gè)任務(wù)。
// 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'
}
}
啟用和禁用任務(wù)
每個(gè)任務(wù)都有一個(gè)enabled默認(rèn)為的標(biāo)志true。將其設(shè)置為false阻止執(zhí)行任何任務(wù)動作。禁用的任務(wù)將標(biāo)記為“跳過”。
task disableMe {
doLast {
println 'This should not be printed if the task is disabled.'
}
}
disableMe.enabled = false
任務(wù)超時(shí)
每個(gè)任務(wù)都有一個(gè)timeout可用于限制其執(zhí)行時(shí)間的屬性。當(dāng)任務(wù)達(dá)到超時(shí)時(shí),其任務(wù)執(zhí)行線程將被中斷。該任務(wù)將被標(biāo)記為失敗。終結(jié)器任務(wù)仍將運(yùn)行。如果--continue使用,其他任務(wù)可以在此之后繼續(xù)運(yùn)行。不響應(yīng)中斷的任務(wù)無法超時(shí)。Gradle 的所有內(nèi)置任務(wù)均會及時(shí)響應(yīng)超時(shí)
task hangingTask() {
doLast {
Thread.sleep(100000)
}
timeout = Duration.ofMillis(500)
}
任務(wù)規(guī)則
有時(shí)您想執(zhí)行一個(gè)任務(wù),該任務(wù)的行為取決于較大或無限數(shù)量的參數(shù)值范圍。提供此類任務(wù)的一種非常好的表達(dá)方式是任務(wù)規(guī)則:
// gradle -q pingServer1
tasks.addRule("Pattern: ping<ID>") { String taskName ->
if (taskName.startsWith("ping")) {
task(taskName) {
doLast {
println "Pinging: " + (taskName - 'ping')
}
}
}
}
規(guī)則不僅在從命令行調(diào)用任務(wù)時(shí)使用。您還可以在基于規(guī)則的任務(wù)上創(chuàng)建 dependsOn 關(guān)系:
// gradle -q groupPing
tasks.addRule("Pattern: ping<ID>") { String taskName ->
if (taskName.startsWith("ping")) {
task(taskName) {
doLast {
println "Pinging: " + (taskName - 'ping')
}
}
}
}
task groupPing {
dependsOn pingServer1, pingServer2
}
如果運(yùn)行“gradle -q tasks”,將找不到名為“pingServer1”或“pingServer2”的任務(wù),但是此腳本正在根據(jù)運(yùn)行這些任務(wù)的請求執(zhí)行邏輯。
Finalizer tasks
當(dāng)計(jì)劃運(yùn)行終結(jié)任務(wù)時(shí),F(xiàn)inalizer tasks 會自動添加到任務(wù)圖中。
//gradle -q taskX
task taskX { doLast { println 'taskX' } } task taskY { doLast { println 'taskY' } } taskX.finalizedBy taskY
即使終結(jié)任務(wù)失敗,也將執(zhí)行 Finalizer tasks。
// gradle -q taskX
task taskX {
doLast {
println 'taskX'
throw new RuntimeException()
}
}
task taskY {
doLast {
println 'taskY'
}
}
taskX.finalizedBy taskY
運(yùn)行結(jié)果:
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
在構(gòu)建創(chuàng)建無論構(gòu)建失敗還是成功都必須清除的資源的情況下,終結(jié)器任務(wù)很有用。這種資源的一個(gè)示例是一個(gè)Web容器,它在集成測試任務(wù)之前啟動,并且即使某些測試失敗,也應(yīng)始終將其關(guān)閉。
要指定終結(jié)器任務(wù),請使用Task.finalizedBy(java.lang.Object ...)方法。此方法接受Task實(shí)例,任務(wù)名稱或Task.dependsOn(java.lang.Object…)接受的任何其他輸入。
到此,關(guān)于 task 的講解到這里就結(jié)束了。
總結(jié)
以上是生活随笔為你收集整理的Gradle 使用教程之 Task 详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是1st tier conferen
- 下一篇: Shell三元表达式