1. 程式人生 > >Gradle 理解 (三):Task

Gradle 理解 (三):Task

task在gradle佔有很重要的地位,因為在gradle中任何執行操作都是通過task來執行。task可以理解成任務,作用就是執行某些指定的操作。

以Android為例,Gradle構建編譯一個Android專案的時候,需要執行很多操作流程。整個過程可以通過命令列gradle assembleRelease --info 來看一下:

我們會看到很多task,這些task就是一個一個任務,裡面執行特定的操作流程,比如preBuild負責準備配置檔案,preReleaseUnitTestBuild負責準備release下的單元測試構建,generateReleaseResources準備資原始檔等等,一系列的task執行起來,構建一個完整的apk檔案。

一. project

在深入理解task之前,還需要理解另外一個重要而又跟task很大關聯的東西:project。

每個可以構建編譯的模組就是一個project。還是以Android為例,無論是library,application都是一個project,更簡單的理解就是看這個模組是否有build.gradle檔案,如果有那麼就是一個project。因為project有構建編譯等其他的需求,那麼自然就需要task,所以project裡面包含至少一個task。

為什麼說有的build.gradle的模組就是一個project?因為Gradle會預設為每個build.gradle根據配置分配一個project物件,所有我們也能在build.gradle裡面預設使用project物件及相對應的API。在build.gradle中,你可以通過API來訪問gradle的所有特性,比如task的建立以及依賴管理,在實際開發中,你也會接觸到很多API。

下面列出一個project的常用的API圖:

1543923049799

這裡面的API我們都可以直接在build.gradle裡面直接使用,而且不需要例項化project物件,因為每個build.gradle都是分配一個project物件。

通過以上,大概也知道project以及project和task之間的關係了。如果還是不明白,打個比喻:project就是一個公司部門,task就是部分的員工,project的運作只有靠一個個task各司其職才能運作起來。

二. task

2.1 action

上面我們提到task的作用就是執行操作,那麼執行操作的動作也就是放置構建邏輯的地方應該如何新增呢?task有個概念-action,就是要執行的動作,內部通過一個集合管理action,task執行的時候像訊息佇列一樣,一個一個action消費執行掉。task提供了doFirst和doLast兩個函式來新增action:doFirst ,doLast ,用於新增需要最先執行的Action和需要和需要最後執行的Action。 看個例子:

task helllTask {
    doFirst {
        println "Task doFirst"
    }

    doLast {
        println "Task doLast"

    }
}

我們給helllTask上新增兩個action,分別是

{
        println "Task doFirst"
}
{
        println "Task doLast"
}

還是套用之前的部分跟員工的例子,task是一個一個員工的話,那麼action就是分配的任務,分別可以選擇上班的時候(doFirst)做跟下班的時候做(doLast)。

2.2 依賴

task 有時候需要執行很多很多操作,有些操作跟其他的task是一樣的,這時候就可以將這些相同操作的task抽取出來一個基礎的task,然後其他的task分別依賴這個基礎的task。

task One {
    doFirst {
        println "一言不合先來個1"
    }

}

task Two {
    doFirst {
        println "一言不合先來個2"
    }

}


task Three(dependsOn: [One, Two]) {
    doFirst {
        println "我需要1跟2"
    }
}

終端執行:gradle Three

得到:

> Task :One
一言不合先來個1

> Task :Two
一言不合先來個2

> Task :Three
我需要1跟2

Three依賴了One, Two,在執行的時候先執行One,Two再執行Three。但是注意一個點,不要被dependsOn: [One, Two] 這樣的寫法迷惑了,不要以為依賴的執行順序就是從左到右執行,正確的是依賴的執行順序是不確定。

task One {
    doFirst {
        println "一言不合先來個1"
    }

}

task Two {
    doFirst {
        println "一言不合先來個2"
    }

}

task Four {
    doFirst {
        println "一言不合先來個4"
    }

}


task Three(dependsOn: [One, Two,Four]) {
    doFirst {
        println "我需要1跟2跟4"
    }
}

終端執行:gradle Three

得到:

> Task :Four
一言不合先來個4

> Task :One
一言不合先來個1

> Task :Two
一言不合先來個2

> Task :Three
我需要1跟2跟4

雖然Four是放在最右邊的,但是執行卻是第一個執行的。為什麼task依賴執行是無序呢?因為這些依賴task執行的時候是並行的,這樣在編譯構建的時候,並行執行能夠極大的節約編譯執行時間。

這時候你可能會想,如果需要指定一個task一定要執行在另外一個task前面呢,比如先執行單元測試,再執行打包。

這種需求是正常的,gradle肯定也是考慮到的。要實現task順序,有兩種方式:

  • shouldRunAfter/mustRunAfter

taskA.shouldRunAfter(taskB):表示taskB應該在taskA執行後執行。主要這裡是should,不是必須要的,也就是還是可能會出現不會按照預設那樣執行。

taskA.mustRunAfter(taskB):表示taskB必須在taskA執行後執行。這裡是must,一定會按照預設那樣執行。

  • finalizedBy

    finalizedBy 也是task的終結器。就是指定一個task在另外一個task結束後必須執行。

    task A {
        doLast {
    
            print "AAAAAA"
        }
    }
    
    task B {
        doLast {
            print "BBBBB"
        }
    }
    
    
    A.finalizedBy B
    

    終端執行:gradle A

    Task :A
    
    AAAAAA
    
    Task :B
    
    BBBBB
    
    

2.3 生命週期

很多框架跟元件都有自己的生命週期,Gradle也有生命週期。Gradle生命週期分為三個部分:初始化階段,配置階段,執行階段。

  • 初始化極端

    在初始化階段,Gradle為每個build.gradle分配一個project例項物件,初始化階段相關類似setting.gradle

    的指令碼檔案 ,然後根據正在執行的專案,找出哪些專案依賴需要參與到構建中來。

  • 配置階段

    初始化階段結束後緊跟就是配置階段。配置階段的任務是執行各專案下的build.gradle指令碼,完成Project的配置,並且構造Task任務依賴關係圖以便在執行階段按照依賴關係執行Task。 Gradle構造了一個模型來表示任務,並且參與到任務構建中來。增量式構建特性決定了模型中的task是否需要被執行。注意,專案中每一次構建的任何配置程式碼都可以被執行。

  • 執行階段

    在執行階段,就是將配置階段task關係依賴順序進行執行。因為是增量構建,如果一個task如果被認為沒有修改過則不會執行。