亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android開發(fā)之Gradle?進階Tasks深入了解

 更新時間:2022年08月24日 14:14:11   作者:程序員江同學  
這篇文章主要為大家介紹了Android開發(fā)之Gradle?進階Tasks深入了解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

Gradle自定義Task看起來非常簡單,通過tasks.register等API就可以輕松實現(xiàn)。但實際上為了寫出高效的,可緩存的,不拖慢編譯速度的task,還需要了解更多知識。

本文主要包括以下內(nèi)容:

  • 定義Task
  • 查找Task
  • 配置Task
  • 將參數(shù)傳遞給Task構造函數(shù)
  • Task添加依賴
  • Task排序
  • Task添加說明
  • 跳過Task
  • Task支持增量編譯
  • Finalizer Task

定義Task

如上所說,自定義Task一般可以通過register API實現(xiàn)

tasks.register("hello") {
    doLast {
        println("hello")
    }
}
tasks.register<Copy>("copy") {
    from(file("srcDir"))
    into(buildDir)
}

如果是kotlin或者kts中,也可以通過代理來實現(xiàn)

val hello by tasks.registering {
    doLast {
        println("hello")
    }
}
val copy by tasks.registering(Copy::class) {
    from(file("srcDir"))
    into(buildDir)
}

register與create的區(qū)別

除了上面介紹的register,其實create也可以用于創(chuàng)建Task,那么它們有什么區(qū)別呢?

  • 通過register創(chuàng)建時,只有在這個task被需要時才會真正創(chuàng)建與配置該Task(被需要是指在本次構建中需要執(zhí)行該Task)
  • 通過create創(chuàng)建時,則會立即創(chuàng)建與配置該Task

總得來說,通過register創(chuàng)建Task性能更好,更推薦使用

查找Task

我們有時需要查找Task,比如需要配置或者依賴某個Task,我們可以通過named方法來查找對應名字的task

tasks.register("hello")
tasks.register<Copy>("copy")
println(tasks.named("hello").get().name) // or just 'tasks.hello' if the task was added by a plugin
println(tasks.named<Copy>("copy").get().destinationDir)

也可以使用tasks.withType()方法來查找特定類型的Task

tasks.withType<Tar>().configureEach {
    enabled = false
}
tasks.register("test") {
    dependsOn(tasks.withType<Copy>())
}

除了上述方法,也可以通過tasks.getByPath()方法來查找task,不過這種方式破壞了configuration avoidance和project isolation,因此不被推薦使用

配置Task

在創(chuàng)建了Task之后,我們常常需要配置Task

我們可以在查找到Task之后進行配置

tasks.named<Copy>("myCopy") {
    from("resources")
    into("target")
    include("**/*.txt", "**/*.xml", "**/*.properties")
}

我們還可以將Task引用存儲在變量中,并用于稍后在腳本中進一步配置任務。

val myCopy by tasks.existing(Copy::class) {
    from("resources")
    into("target")
}
myCopy {
    include("**/*.txt", "**/*.xml", "**/*.properties")
}

我們也可以在定義Task時進行配置,這也是最常用的一種

tasks.register<Copy>("copy") {
   from("resources")
   into("target")
   include("**/*.txt", "**/*.xml", "**/*.properties")
}

將參數(shù)傳遞給Task構造函數(shù)

除了在Task創(chuàng)建后配置參數(shù),我們也可以將參數(shù)傳遞給Task的構建函數(shù),為了實現(xiàn)這點,我們必須使用@Inject注解

abstract class CustomTask @Inject constructor(
    private val message: String,
    private val number: Int
) : DefaultTask()

然后,我們可以創(chuàng)建一個Task,在參數(shù)列表的末尾傳遞構造函數(shù)參數(shù)。

tasks.register<CustomTask>("myTask", "hello", 42)

需要注意的是,在任何情況下,作為構造函數(shù)參數(shù)傳遞的值都必須是非空的。如果您嘗試傳遞一個null值,Gradle 將拋出一個NullPointerException指示哪個運行時值是null.

Task添加依賴

有幾種方法可以定義Task的依賴關系,首先我們可以通過名稱定義依賴項

project("project-a") {
    tasks.register("taskX") {
        dependsOn(":project-b:taskY")
        doLast {
            println("taskX")
        }
    }
}
project("project-b") {
    tasks.register("taskY") {
        doLast {
            println("taskY")
        }
    }
}

其次我們也可以通過Task對象定義依賴項

val taskX by tasks.registering {
    doLast {
        println("taskX")
    }
}
val taskY by tasks.registering {
    doLast {
        println("taskY")
    }
}
taskX {
    dependsOn(taskY)
}

還有一些更高端的用法,我們可以用provider懶加載塊來定義依賴項,在evaluated階段,provider被傳遞給正在計算依賴的task

provider塊應返回單個對象Task或Task對象集合,然后將其視為任務的依賴項,如下所示:taskx添加了所有以lib開頭的對象

val taskX by tasks.registering {
    doLast {
        println("taskX")
    }
}
// Using a Gradle Provider
taskX {
    dependsOn(provider {
        tasks.filter { task -> task.name.startsWith("lib") }
    })
}
tasks.register("lib1") {
    doLast {
        println("lib1")
    }
}tasks.register("lib2") {
    doLast {
        println("lib2")
    }
}
tasks.register("notALib") {
    doLast {
        println("notALib")
    }
}

Task排序

有時候,兩個task之間沒有依賴關系,但是對兩個task的執(zhí)行順序卻有所要求

任務排序和任務依賴之間的主要區(qū)別在于,排序規(guī)則不會影響將執(zhí)行哪些任務,只會影響它們的執(zhí)行順序。

任務排序在許多場景中都很有用:

  • 強制執(zhí)行任務的順序:例如,build 永遠不會在clean 之前運行。
  • 在構建的早期運行構建驗證:例如,在開始發(fā)布構建工作之前驗證我是否擁有正確的憑據(jù)。
  • 通過在長時間驗證任務之前運行快速驗證任務來更快地獲得反饋:例如,單元測試應該在集成測試之前運行。
  • 聚合特定類型的所有任務的結果的任務:例如測試報告任務組合所有已執(zhí)行測試任務的輸出。

gradle提供了兩個可用的排序規(guī)則:mustRunAfter 和 shouldRunAfter

當您使用mustRunAfter排序規(guī)則時,您指定taskB必須始終在taskA之后運行,這表示為taskB.mustRunAfter(taskA)

而shouldRunAfter規(guī)則理加弱化,因為在兩種情況下這條規(guī)則會被忽略。一是使用這條規(guī)則會導致先后順序成環(huán)的情況,二是當并行執(zhí)行task,并且任務的所有依賴關系都已經(jīng)滿足時,那么無論它的shouldRunAfter依賴關系是否已經(jīng)運行,這個任務都會運行。

因此您應該在排序有幫助但不是嚴格要求的情況下使用shouldRunAfter

示例如下:

val taskX by tasks.registering {
    doLast {
        println("taskX")
    }
}
val taskY by tasks.registering {
    doLast {
        println("taskY")
    }
}
// mustRunAfter 
taskY {
    mustRunAfter(taskX)
}
// shouldRunAfter
taskY {
    shouldRunAfter(taskX)
}

需要注意的是,B.mustRunAfter(A)或B.shouldRunAfter(A)并不意味著任務之間存在任何執(zhí)行依賴關系:

我們可以獨立執(zhí)行A或者任務B。排序規(guī)則僅在兩個任務都計劃執(zhí)行時才有效。

Task添加說明

您可以為Task添加說明。執(zhí)行時gradle tasks時會顯示此說明。

tasks.register<Copy>("copy") {
   description = "Copies the resource directory to the target directory."
   from("resources")
   into("target")
   include("**/*.txt", "**/*.xml", "**/*.properties")
}

跳過Task

gradle提供了多種方式來跳過task的執(zhí)行

使用onlyIf

你可以通過onlyIf為任務的執(zhí)行添加條件,如果任務應該執(zhí)行,則應該返回 true,如果應該跳過任務,則返回 false

val hello by tasks.registering {
    doLast {
        println("hello world")
    }
}
hello {
    onlyIf { !project.hasProperty("skipHello") }
}

Output of gradle hello -PskipHello
> gradle hello -PskipHello
> Task :hello SKIPPED 

如上所示,hello任務被跳過了

使用 StopExecutionException

如果跳過任務邏輯不能使用onlyIf實現(xiàn),您可以使用StopExecutionException。如果某個Action拋出此異常,則跳過該Action的進一步執(zhí)行以及該任務的任何后續(xù)Action的執(zhí)行。構建繼續(xù)執(zhí)行下一個任務。

val compile by tasks.registering {
    doLast {
        println("We are doing the compile.")
    }
}
compile {
    doFirst {
        // Here you would put arbitrary conditions in real life.
        if (true) {
            throw StopExecutionException()
        }
    }
}
tasks.register("myTask") {
    dependsOn(compile)
    doLast {
        println("I am not affected")
    }
}

禁用與啟用Task

每個任務都有一個enabled的標志位,默認為true。將其設置為false可以阻止執(zhí)行任何Task的執(zhí)行。禁用的任務將被標記為 SKIPPED。

val disableMe by tasks.registering {
    doLast {
        println("This should not be printed if the task is disabled.")
    }
}
disableMe {
    enabled = false
}

Task超時

每個Task都有一個timeout屬性,可用于限制其執(zhí)行時間。當一個任務達到它的超時時間時,它的任務執(zhí)行線程被中斷。該任務將被標記為失敗。但是Finalizer Task任務仍將運行。

如果構建時使用了--continue參數(shù),其他任務可以在它之后繼續(xù)運行。不響應中斷的task不能超時。Gradle 的所有內(nèi)置task都會及時響應超時

Task支持增量編譯

任何構建工具的一個重要部分是避免重復工作。在編譯過程中,就是在編譯源文件后,除非發(fā)生了影響輸出的更改(例如源文件的修改或輸出文件的刪除),無需重新編譯它們。因為編譯可能會花費大量時間,因此在不需要時跳過該步驟可以節(jié)省大量時間。

Gradle 支持增量構建,當您運行構建時,有些Task被標記為UP-TO-DATE,這就是增量編譯生效了

那么Gradle增量編譯如何工作?自定義Task如何支持增量編譯?我們一起來看看

Task的輸入輸出

Task最基本的功能就是接受一些輸入,進行一系列運算后生成輸出。比如在編譯過程中,Java源文件是輸入,生成的classes文件是輸出。其他輸入可能包括諸如是否應包含調(diào)試信息之類的內(nèi)容。

task輸入的一個重要特征是它會影響一個或多個輸出,從上圖中可以看出。根據(jù)源文件的內(nèi)容和target jdk版本,會生成不同的字節(jié)碼。這使他們成為task輸入。

但是編譯期的一些其他屬性,比如編譯最大可用內(nèi)存,由memoryMaximumSize屬性決定,memoryMaximumSize對生成的字節(jié)碼沒有影響。因此,memoryMaximumSize不是task輸入,它只是一個內(nèi)部task屬性。

作為增量構建的一部分,Gradle 會檢查自上次構建以來是task的輸入或輸出有沒有發(fā)生變化。如果沒有,Gradle 可以認為task是最新的,因此跳過執(zhí)行其action。需要注意的是,除非task至少有一個task輸出,否則增量構建將不起作用

總得來說:

您需要告訴 Gradle 哪些task屬性是輸入,哪些是輸出。

如果task屬性影響輸出,請務必將其注冊為輸入,否則該任務將被認為是最新的而不是最新的。

相反,如果屬性不影響輸出,則不要將其注冊為輸入,否則任務可能會在不需要時執(zhí)行。

還要注意可能為完全相同的輸入生成不同輸出的非確定性task:不應將這些任務配置為增量構建,因為最新檢查將不起作用。

接下來讓我們看看如何將task屬性注冊為輸入和輸出。

自定義task類型

為了讓自定義task支持增量編譯,只需要以下兩個步驟

  • 為每個task輸入和輸出創(chuàng)建類型化屬性(通過 getter 方法)
  • 為每個屬性添加適當?shù)淖⒔?/li>

Gradle 支持四種主要的輸入和輸出類型:

  • 簡單值
    例如字符串和數(shù)字類型。更一般地說,任何一個實現(xiàn)了Serializable的類型。
  • 文件系統(tǒng)類型
    包括RegularFile,Directory和標準File類,也包括 Gradle 的FileCollection類型的派生類,以及任何可以被Project.file(java.lang.Object)和Project.files(java.lang.Object...)方法接收的參數(shù)
  • 依賴解析結果
    這包括包含Artifact元數(shù)據(jù)的ResolvedArtifactResult類型和包含依賴圖的ResolvedComponentResult類型。請注意,它們僅支持包裝在Provider中.
  • 包裝類型
    不符合其他幾個類型但具有自己的輸入或輸出屬性的自定義類型。task的輸入或輸出包裝在這些自定義類型中。

接下來我們看個例子

假設您有一個task處理不同類型的模板,例如 FreeMarker、Velocity、Moustache 等。它獲取模板源文件并將它們與一些模型數(shù)據(jù)結合以生成不同結果。

此任務將具有三個輸入和一個輸出:

  • 模板源文件
  • 模型數(shù)據(jù)
  • 模板引擎
  • 輸出文件的寫入位置

在編寫自定義task類時,我們很容易通過注解將屬性注冊為輸入或輸出

public abstract class ProcessTemplates extends DefaultTask {
    @Input
    public abstract Property<TemplateEngineType> getTemplateEngine();
    @InputFiles
    public abstract ConfigurableFileCollection getSourceFiles();
    @Nested
    public abstract TemplateData getTemplateData();
    @OutputDirectory
    public abstract DirectoryProperty getOutputDir();
    @TaskAction
    public void processTemplates() {
        // ...
    }
}
public abstract class TemplateData {
    @Input
    public abstract Property<String> getName();
    @Input
    public abstract MapProperty<String, String> getVariables();
}

可以看出,我們定義了3個輸入,一個輸出

  • templateEngine,表示使用什么模板引擎,我們傳入一個枚舉類型,枚舉類型都實現(xiàn)了Serializable,因此可作為輸入
  • sourceFiles,表示源文件,我們傳入FileCollection作為輸入
  • templateData,表示模型數(shù)據(jù),自定義類型,在它的內(nèi)部包裝了真正的輸入,通過@Nested注解表示
  • outputDir,表示輸出目錄,表示單個目錄的屬性需要@OutputDirectory注解

當我們重復運行以上task之后,就可以看到以下輸出

> gradle processTemplates
> Task :processTemplates UP-TO-DATE
BUILD SUCCESSFUL in 0s
3 actionable tasks: 3 up-to-date

如上所示,task在執(zhí)行過程中會判斷輸入輸出有沒有發(fā)生變化,由于task的輸入輸出都沒有發(fā)生變化,該task可以直接跳過,展示為up-to-date

除了上述幾種注解,還有其他常用注解如@Internal,@Optional,@Classpath等,具體可查看文檔:Incremental build property type annotations

聲明輸入輸出的好處

一旦你聲明了一個task的正式輸入和輸出,Gradle 就可以推斷出關于這些屬性的一些事情。例如,如果一個task的輸入設置為另一個task的輸出,這意味著第一個task依賴于第二個,gradle可以推斷出這一點并添加隱式依賴

推斷task依賴關系

想象一個歸檔task,會將processTemplates task的輸出歸檔??梢钥吹綒w檔task顯然需要processTemplates首先運行,因此可能會添加顯式的dependsOn. 但是,如果您像這樣定義歸檔task:

tasks.register<Zip>("packageFiles") {
    from(processTemplates.map {it.outputs })
}

Gradle 會自動使packageFiles依賴processTemplates。它可以這樣做是因為它知道 packageFiles 的輸入之一需要 processTemplates 任務的輸出。我們稱之為推斷的task依賴。

上面的例子也可以寫成

tasks.register<Zip>("packageFiles2") {
    from(processTemplates)
}

這是因為from()方法可以接受task對象作為參數(shù)。然后在幕后,from()使用project.files()方法包裝參數(shù),進而將task的正式輸出轉(zhuǎn)化為文件集合

輸入和輸出驗證

增量構建注解為 Gradle 提供了足夠的信息來對帶注解的屬性執(zhí)行一些基本驗證。它會在task執(zhí)行之前對每個屬性執(zhí)行以下操作:

  • @InputFile- 驗證屬性是否有值,并且路徑是否對應于存在的文件(不是目錄)。
  • @InputDirectory- 與@InputFile相同,但路徑必須對應于目錄。
  • @OutputDirectory- 驗證路徑是否是個目錄,如果該目錄尚不存在,則創(chuàng)建該目錄。

如果一個task在某個位置產(chǎn)生輸出,而另一個任務task將其作為輸入使用,則 Gradle 會檢查消費者任務是否依賴于生產(chǎn)者任務。當生產(chǎn)者和消費者任務同時執(zhí)行時,構建就會失敗。

此類驗證提高了構建的穩(wěn)健性,使您能夠快速識別與輸入和輸出相關的問題。

您偶爾會想要禁用某些驗證,特別是當輸入文件可能實際上不存在時。這就是 Gradle 提供@Optional注釋的原因:您使用它來告訴 Gradle 特定輸入是可選的,因此如果相應的文件或目錄不存在,則構建不應失敗。

并行task

定義task輸入和輸出的另一個好處是:當使用--parallel選項時,Gradle 可以使用此信息來決定如何運行task。

例如,Gradle 將在選擇下一個要運行的任務時檢查task的輸出,并避免并發(fā)執(zhí)行寫入同一輸出目錄的任務。

同樣,當另一個task正在運行消耗或創(chuàng)建一些文件時,Gradle 將使用有關task銷毀哪些文件的信息(例如,由Destroys注釋)來避免運行刪除這些文件的task,反之亦然。

它還可以確定創(chuàng)建一組文件的task已經(jīng)運行,并且使用這些文件的task尚未運行,并且將避免在這中間運行刪除這些文件的task。

總得來說,通過以這種方式提供task的輸入和輸出信息,Gradle 可以推斷task之間的創(chuàng)建/消費/銷毀關系,并可以確保task執(zhí)行不會違反這些關系。

增量編譯原理解析

上面我們介紹了如何自定義一個支持增量編譯的task,那么它的原理是什么呢?

在第一次執(zhí)行task之前,Gradle 會獲取輸入的指紋。該指紋包含輸入文件的路徑和每個文件內(nèi)容的哈希值。Gradle 然后執(zhí)行task。如果任務成功完成,Gradle 會獲取輸出的指紋。該指紋包含一組輸出文件和每個文件內(nèi)容的哈希值。Gradle 會在下次執(zhí)行task時保留兩個指紋。

之后每次執(zhí)行task之前,Gradle 都會獲取輸入和輸出的新指紋。如果新指紋與之前的指紋相同,Gradle 會假定輸出是最新的并跳過該task。如果它們不相同,Gradle 將執(zhí)行task。Gradle 會在下次執(zhí)行task時保留兩個指紋。

如果文件的統(tǒng)計信息(即lastModified和size)沒有改變,Gradle 將重用上次運行的文件指紋。這意味著當文件的統(tǒng)計信息沒有更改時,Gradle 不會檢測到更改。

Gradle 還將task的代碼視為task輸入的一部分。當task、其操作或其依賴項在執(zhí)行之間發(fā)生變化時,Gradle 會認為該task已過期。

Gradle 了解文件屬性(例如,包含 Java classpath 的屬性)是否是順序敏感的。在比較此類屬性的指紋時,即使文件順序發(fā)生變化也會導致task過時。

請注意,如果task指定了輸出目錄,則自上次執(zhí)行以來添加到該目錄的任何文件都將被忽略,并且不會導致任務過期。這是因為不相關的任務可以共享一個輸出目錄而不會相互干擾。如果由于某種原因這不是您想要的行為,請考慮使用TaskOutputs.upToDateWhen(groovy.lang.Closure)

一些高端操作

上面介紹的內(nèi)容涵蓋了您將遇到的大多數(shù)用例,但有些場景需要特殊處理

將@OutputDirectory鏈接到@InputFiles

當您想將一個task的輸出鏈接到另一個task的輸入時,類型通常匹配,例如,F(xiàn)ile可以將輸出屬性分配給File輸入。

不幸的是,當您希望將一個task的@OutputDirectory中的文件作為另一個task的@InputFiles屬性(類型FileCollection)的源時,這種方法就會失效。

例如,假設您想使用 Java 編譯task的輸出(通過destinationDir屬性)作為自定義task的輸入,該task檢測一組包含 Java 字節(jié)碼的文件。這個自定義task,我們稱之為Instrument,有一個使用@InputFiles注解的classFiles屬性。您最初可能會嘗試像這樣配置task:

tasks.register<Instrument>("badInstrumentClasses") {
    classFiles.from(fileTree(tasks.compileJava.map { it.destinationDir }))
    destinationDir.set(file(layout.buildDirectory.dir("instrumented")))
}

這段代碼沒有明顯的問題,但是您如果實際運行的話可以看到compileJava并沒有執(zhí)行。在這種情況下,您需要通過dependsOn在instrumentClasses和compileJava之間添加顯式依賴。因為使用fileTree()意味著 Gradle 無法推斷task依賴本身。

一種解決方案是使用TaskOutputs.files屬性,如以下示例所示:

tasks.register<Instrument>("instrumentClasses") {
    classFiles.from(tasks.compileJava.map { it.outputs.files })
    destinationDir.set(file(layout.buildDirectory.dir("instrumented")))
}

或者,您可以使用project.files(),project.layout.files(),project.objects.fileCollection()來代替project.fileTree()

tasks.register<Instrument>("instrumentClasses2") {
    classFiles.from(layout.files(tasks.compileJava))
    destinationDir.set(file(layout.buildDirectory.dir("instrumented")))
}

請記住files(),layout.files()和objects.fileCollection()可以將task作為參數(shù),而fileTree()不能。

這種方法的缺點是源task的所有文件輸出都成為目標task的輸入文件。如果源task只有一個基于文件的輸出,就像JavaCompile一樣,那很好。但是如果你必須在多個輸出屬性中選擇一個,那么你需要明確告訴 Gradle 哪個task使用以下builtBy方法生成輸入文件:

tasks.register<Instrument>("instrumentClassesBuiltBy") {
    classFiles.from(fileTree(tasks.compileJava.map { it.destinationDir }) {
        builtBy(tasks.compileJava)
    })
    destinationDir.set(file(layout.buildDirectory.dir("instrumented")))
}

當然你也可以通過dependsOn添加明確的task依賴,但是上面的方法提供了更多的語義,解釋了為什么compileJava必須預先運行。

禁用up-to-date檢查

Gradle 會自動處理對輸出文件和目錄的up-to-date檢查,但如果task輸出完全是另一回事呢?也許它是對 Web 服務或數(shù)據(jù)庫表的更新?;蛘哂袝r你有一個應該始終運行的task。

這就是doNotTrackState的作用,可以使用它來完全禁用task的up-to-date檢查,如下所示:

tasks.register<Instrument>("alwaysInstrumentClasses") {
    classFiles.from(layout.files(tasks.compileJava))
    destinationDir.set(file(layout.buildDirectory.dir("instrumented")))
    doNotTrackState("Instrumentation needs to re-run every time")
}

如果你的自定義task需要始終運行,那么您也可以在任務類上使用注解@UntrackedTaskTask

提供自定義up-to-date檢查

Gradle 會自動處理對輸出文件和目錄的up-to-date檢查,但如果task輸出是對 Web 服務或數(shù)據(jù)庫表的更新。在這種情況下,Gradle 無法知道如何檢查task是否是up-to-date的。

這是就是TaskOutputs.upToDateWhen方法的作用,使用它我們就可以自定義up-to-date檢查的邏輯。例如,您可以從數(shù)據(jù)庫中讀取數(shù)據(jù)庫模式的版本號。或者,您可以檢查數(shù)據(jù)庫表中的特定記錄是否存在或已更改。

請注意,up-to-date檢查應該節(jié)省您的時間。不要添加比task的標準執(zhí)行花費更多時間的檢查。事實上,如果一個task經(jīng)常需要運行,因為它很少是up-to-date的,那么它可能根本不值得進行up-to-date檢查,如禁用up-to-date中所述。

一個常見的錯誤是使用upToDateWhen()而不是Task.onlyIf(). 如果您想根據(jù)與task輸入和輸出無關的某些條件跳過任務,那么您應該使用onlyIf(). 例如,如果您想在設置或未設置特定屬性時跳過task

Finalizer Task

我們常常使用dependsOn來在一個task之前做一些工作,但如果我們想要在task執(zhí)行之后做一些操作,該怎么實現(xiàn)呢?

這里我們可以用到finalizedBy方法

val taskX by tasks.registering {
    doLast {
        println("taskX")
    }
}
val taskY by tasks.registering {
    doLast {
        println("taskY")
    }
}
taskX { finalizedBy(taskY) }

如上所示,taskY將在taskX之后執(zhí)行,需要注意的是finalizedBy并不是依賴關系,就算taskX執(zhí)行失敗,taskY也將正常執(zhí)行

Finalizer task在構建創(chuàng)建的資源無論構建失敗還是成功都必須清理的情況下很有用,一個示例是在集成測試任務中啟動的 Web 容器,即使某些測試失敗,也應該始終關閉它。這樣看來finalizedBy類似java中的finally

要指定Finalizer task,請使用Task.finalizedBy(java.lang.Object...?)方法。此方法接受task實例、task名稱或Task.dependsOn(java.lang.Object...?)接受的任何其他輸入

總結

到這里這篇文章已經(jīng)相當長了,gradle自定義task上手非常簡單,但實際上有非常多的細節(jié),尤其是要支持增量編譯時??偟脕碚f,為了寫出高效的,可緩存的,不拖慢編譯速度的task,還是有必要了解一下這些知識的

參考資料

docs.gradle.org/current/use…

以上就是Android開發(fā)之Gradle 進階Tasks深入了解的詳細內(nèi)容,更多關于Android開發(fā)Gradle Tasks的資料請關注腳本之家其它相關文章!

相關文章

  • android編程實現(xiàn)系統(tǒng)圖片剪裁的方法

    android編程實現(xiàn)系統(tǒng)圖片剪裁的方法

    這篇文章主要介紹了android編程實現(xiàn)系統(tǒng)圖片剪裁的方法,涉及Android針對圖片的獲取、修改、保存等操作的相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-11-11
  • Android Studio多渠道打包套路

    Android Studio多渠道打包套路

    最近有好多朋友向小編咨詢Android Studio多渠道的打包方法,今天小編給大家分享Android Studio多渠道打包套路,需要的朋友參考下吧
    2017-11-11
  • Android實現(xiàn)頂部導航菜單左右滑動效果

    Android實現(xiàn)頂部導航菜單左右滑動效果

    這篇文章主要為大家詳細介紹了Android實現(xiàn)頂部導航菜單左右滑動效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-06-06
  • 理解Android系統(tǒng)Binder機制

    理解Android系統(tǒng)Binder機制

    這篇文章主要為大家介紹了Android系統(tǒng)Binder機制,幫助大家理解Binder機制,感興趣的朋友可以參考一下
    2016-05-05
  • RecyclerView索引溢出異常的解決方法

    RecyclerView索引溢出異常的解決方法

    本篇文章主要介紹了RecyclerView索引溢出異常的解決方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • 詳解Android應用main函數(shù)的調(diào)用

    詳解Android應用main函數(shù)的調(diào)用

    Android常識,App主線程初始化了Looper,調(diào)用prepare的地方是ActivityThread.main函數(shù)。問題來了,App的main函數(shù)在哪兒調(diào)用,下面我們來一起學習一下吧
    2019-06-06
  • Android中通過AsyncTask類來制作炫酷進度條的實例教程

    Android中通過AsyncTask類來制作炫酷進度條的實例教程

    這篇文章主要介紹了Android中通過AsyncTask來制作炫酷進度條的實例教程,借助AsyncTask類的線程操作方法來管理異步任務,需要的朋友可以參考下
    2016-05-05
  • Android RxJava異步數(shù)據(jù)處理庫使用詳解

    Android RxJava異步數(shù)據(jù)處理庫使用詳解

    RxJava是一種異步數(shù)據(jù)處理庫,也是一種擴展的觀察者模式。對于Android開發(fā)者來說,使用RxJava時也會搭配RxAndroid,它是RxJava針對Android平臺的一個擴展,用于Android 開發(fā),它提供了響應式擴展組件,使用RxAndroid的調(diào)度器可以解決Android多線程問題
    2022-11-11
  • Android使用Intent.ACTION_SEND分享圖片和文字內(nèi)容的示例代碼

    Android使用Intent.ACTION_SEND分享圖片和文字內(nèi)容的示例代碼

    這篇文章主要介紹了Android使用Intent.ACTION_SEND分享圖片和文字內(nèi)容的示例代碼的實例代碼,具有很好的參考價值,希望對大家有所幫助,一起跟隨小編過來看看吧
    2018-05-05
  • windows10安裝adb/fastboot驅(qū)動超詳細圖文教程

    windows10安裝adb/fastboot驅(qū)動超詳細圖文教程

    這篇文章主要介紹了windows10安裝adb/fastboot超詳細圖文教程,安裝方法也很簡單,只要adb安裝成功,fastboot就安裝好了,文中給大家介紹了問題分析及解決方法,需要的朋友可以參考下
    2023-01-01

最新評論