Gradle構(gòu)建的生命周期和其對(duì)象的理解

文章主題內(nèi)容來自Gradle官方文檔Understanding the Build Lifecycler章節(jié)。通讀完該章節(jié),大大加深了我對(duì)task對(duì)象,project對(duì)象,gradle.build腳本和project對(duì)象的關(guān)系等這3個(gè)概念的理解。

官方文檔地址:https://docs.gradle.org/current/userguide/build_lifecycle.html#sub:building_the_tree

構(gòu)建的不同階段

一個(gè)gradle的構(gòu)建有3個(gè)不同的階段

  1. 初始化(Initialization)

    Gradle支持單和多project的構(gòu)建。在初始化階段,gradle決定了哪一個(gè)或哪些project將要參與到這次構(gòu)建,并且為每個(gè)project創(chuàng)建一個(gè)Project對(duì)象。(注意,一個(gè)project對(duì)應(yīng)一個(gè)build.gradle文件)

  2. 安裝(Configuration)

    在這個(gè)階段,Project對(duì)象被安裝(個(gè)人猜測(cè)是執(zhí)行Project對(duì)象的構(gòu)造函數(shù))。所有參與到這次構(gòu)建的build.gradle腳本文件都會(huì)被執(zhí)行。

  3. 執(zhí)行(Execution)

    在此階段,gradle將會(huì)決定在安裝(Configuration)階段所創(chuàng)建和裝配的tasks的哪些子集tasks要被執(zhí)行。被執(zhí)行的那些task是通過gradle命令的參數(shù)中的task的名字和當(dāng)前在哪個(gè)目錄下來決定的。gradle然后執(zhí)行每一個(gè)被選中的task。

settings.gradle

  1. 除了build.gradle腳本之外,gradle還定義了一個(gè)settings.gradle文件。這個(gè)文件會(huì)在初始化(initialization)階段被執(zhí)行。
  2. 一個(gè)multi-project的構(gòu)建必須有一個(gè)settings.gradle文件在根目錄。因?yàn)楹笳叨x了哪些project(也就是build.gradle腳本)會(huì)參與到這個(gè)構(gòu)建。當(dāng)然,單project(單個(gè)build.gradle腳本)的情況下,settings.gradle可有可無。

3個(gè)不同階段在構(gòu)建中的執(zhí)行順序

1. 定義了3個(gè)task來分別查看他們執(zhí)行時(shí)打印的情況

  1. settings.gradle文件

    println('initialization:settings.gradle被執(zhí)行')
    
  2. build.gradle文件

    println('configuration:build.gradle')
    task configured{
        println('configuration:task configured')
    }
    task A{
        println('configuration:task A')
        doLast{
            println '這里執(zhí)行task:A#doLast'
        }
    }
    task B {
        doFirst{
            println '這里執(zhí)行task:B#doFirst'
        }
        doLast{
            println '這里執(zhí)行task:B#doLast'
        }
        println 'configuration:task B'
    }
    
  3. $ gradle configured

    william@localhost:~/IdeaProjects/1$ gradle configured
    initialization:settings.gradle被執(zhí)行
    
    > Configure project : 
    configuration:build.gradle
    configuration:task configured
    configuration:task A
    configuration:task B
    
  4. $ gradle A

    initialization:settings.gradle被執(zhí)行
    
    > Configure project : 
    configuration:build.gradle
    configuration:task configured
    configuration:task A
    configuration:task B
    
    > Task :A 
    這里執(zhí)行task:A#doLast
    
  5. $ gradle B

    并被添加到Project對(duì)象的一個(gè)字段TaskContainer tasks中,initialization:settings.gradle被執(zhí)行
    
    > Configure project : 
    configuration:build.gradle
    configuration:task configured
    configuration:task A
    configuration:task B
    
    > Task :B 
    這里執(zhí)行task:B#doFirst
    這里執(zhí)行task:B#doLast
    
  6. $ gradle A B

    initialization:settings.gradle被執(zhí)行
    
    > Configure project : 
    configuration:build.gradle
    configuration:task configured
    configuration:task A
    configuration:task B
    
    > Task :A 
    這里執(zhí)行task:A#doLast
    
    > Task :B 
    這里執(zhí)行task:B#doFirst
    這里執(zhí)行task:B#doLast
    

2. 結(jié)論

  1. 初始化階段執(zhí)行settings.gradle腳本中的內(nèi)容,

  2. 安裝階段執(zhí)行build.gradle腳本中除了task.doLast(Closure c)task.doFirst(Closure c)中的所有內(nèi)容。因?yàn)樵诎惭b階段時(shí),該build.gradle文件對(duì)應(yīng)的Project對(duì)象已經(jīng)創(chuàng)建,此時(shí)安裝階段對(duì)應(yīng)的就是Project對(duì)象執(zhí)行其構(gòu)造方法。而上述代碼中在build.gradle中定義了3個(gè)task,那么在安裝階段,這3個(gè)task就會(huì)被創(chuàng)建為3個(gè)task對(duì)象實(shí)例,并且被加入到Project對(duì)象的TaskContainer容器中,此后,這3個(gè)task實(shí)例就作為Project對(duì)象的屬性,可以直接使用了。

  3. 執(zhí)行階段,根據(jù)輸入的task的名稱和相關(guān)依賴(如果存在),去遍歷執(zhí)行對(duì)應(yīng)的task對(duì)象中

    private List<ContextAwareTaskAction> actions=new ArrayList<>();
    

    的所有的方法,而當(dāng)我們調(diào)用doLast或者doFirst都是往那個(gè)ArrayList的頭尾插入由我們傳入的閉包而轉(zhuǎn)化成的Action接口的對(duì)象。而Action接口長這樣:

    @HasImplicitReceiver
    public interface Action<T> {
        void execute(T t);
    }
    

    那么到執(zhí)行階段,這個(gè)task的Action的容器actions就會(huì)被遍歷并調(diào)用每個(gè)Actionexecute(T t)方法。

響應(yīng)build.gradle腳本的生命周期

1. Project的安裝

build.gradle中的內(nèi)容依靠Project對(duì)象的構(gòu)造方法來安裝Project對(duì)象,其中有兩個(gè)回調(diào)暴露給我們,分別是

afterEvalueate(Closure closure)
beforeEvalueate(Closure closure)

它們屬于Project接口中就定義了的方法,因此直接用就行

build.gradle:

afterEvaluate {
    if (it.hasProperty('group')) {
        println('has group')
        it.task('B'){
            doLast{
                println 'execute B'
            }
        }
    } else {
        println('do not have group')
    }
}
task A {
    println(' configure A')
}

執(zhí)行task B

william@localhost:~/IdeaProjects/1$ gradle B

> Configure project : 
configure A
has task A

> Task :B 
execute B


BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed

執(zhí)行結(jié)果如上,Project#afterEvaluated(Closure closure)方法會(huì)在Project對(duì)象全部安裝完之后被調(diào)用,這也是其構(gòu)造方法暴露給客戶端的回調(diào),那么Project對(duì)象的構(gòu)造方法的偽代碼如下:

public ProjectImpl(){
    runClosureBeforeEvalueation();//執(zhí)行安裝前的閉包
    configureCodeInSript();//安裝
    runClosureAfterEvalueation();//執(zhí)行安裝后的閉包    
}

2. Task的創(chuàng)建

在一個(gè)task對(duì)象被添加到一個(gè)Project對(duì)象后,可以立刻收到一個(gè)回調(diào)。

tasks.whenTaskAdded(Closure closure)就可以辦到

build.gradle:

tasks.whenTaskAdded {
    println(it.name)
}
task A {
    println('configure A')
}
task B {
    println 'configure B'
}

執(zhí)行task A

william@localhost:~/IdeaProjects/1$ gradle A

> Configure project : 
A
configure A
B
configure B

3.Task的執(zhí)行

gradle.taskGraph.addTaskExecutionListener(new TaskExecutionListener() {
    @Override
    void beforeExecute(Task task) {
    }
    @Override
    void afterExecute(Task task, TaskState state) {
    }
})

gradle.taskGraph.beforeTask {
}
gradle.taskGraph.afterTask {
}

上面的接口的方法和下面的兩個(gè)閉包方法的作用都是一樣的,我認(rèn)為他們相比于Task#doFirst或者Task#doLast方法的優(yōu)點(diǎn)在于,TaskExecutionGraph的這幾個(gè)方法,是為每一個(gè)安裝了的task對(duì)象都插入一段回調(diào),這段回調(diào)在每一個(gè)task執(zhí)行前后被調(diào)用。


個(gè)人總結(jié):

  1. 在執(zhí)行build.gradle之前,他所對(duì)應(yīng)的Project對(duì)象已然創(chuàng)建,猜測(cè)是在執(zhí)行settings.gradle時(shí)已經(jīng)創(chuàng)建了。
  2. build.gradle中的所有代碼,都作為Project對(duì)象的構(gòu)造函數(shù)一部分而插入構(gòu)造函數(shù)。
  3. 在build.gradle腳本中創(chuàng)建的所有Task對(duì)象都在創(chuàng)建后都被Project對(duì)象的TaskContainer這個(gè)容器對(duì)象引用。
  4. 命令行每執(zhí)行一次gradle命令,就創(chuàng)建一個(gè)程序,從類似java的main方法開始運(yùn)行。創(chuàng)建Project對(duì)象,執(zhí)行build.gradle腳本來安裝(在他的構(gòu)造方法中),而gradle命令后面跟隨的是task的名字,此時(shí)裝配好Project對(duì)象后,根據(jù)傳入的task的名字來去直接執(zhí)行這個(gè)task。當(dāng)task執(zhí)行完畢輸出結(jié)果后,程序結(jié)束,main()方法結(jié)束。
  5. 當(dāng)直接執(zhí)行g(shù)radle命令而不加task,也會(huì)依然會(huì)執(zhí)行build.gradle中為Project裝配而存在的代碼,即此時(shí)Project對(duì)象存在,但是只是不去execute task了。

以上,為總結(jié)和對(duì)gradle運(yùn)行原理的猜想,待考證更多資料后證實(shí)之。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 說明 本文主要從實(shí)現(xiàn)原理和代碼層面介紹Gradle開發(fā)相關(guān)知識(shí)。關(guān)于本文中提到的、Gradle中的基本概念等內(nèi)容,...
    搬磚的小明閱讀 8,145評(píng)論 1 33
  • 構(gòu)建的生命周期 Gradle項(xiàng)目的構(gòu)建分為三個(gè)階段:初始化、配置、執(zhí)行。參考官方手冊(cè) Build Lifecycl...
    十思葉閱讀 3,220評(píng)論 1 6
  • 應(yīng)用拆分 應(yīng)用拆分原則 應(yīng)用拆分思考 Dobbo 和 SpringCloud Dobbo : 分布式服務(wù)框架,提供...
    Marlon666閱讀 493評(píng)論 0 0
  • 《班主任》 自從當(dāng)了班主任 找我干啥的都有 回家收核桃的 看病的 買衣服的 買感冒藥的 還有一個(gè) 成天在九月的天氣...
    莉莉郭閱讀 844評(píng)論 0 0
  • 清明節(jié)來看爺爺奶奶,我爺爺?shù)男愿窬褪悄欠N執(zhí)著不肯聽人勸的那種,身上挺多病的,一只眼睛幾乎看不見了,耳朵也怎么聽見腿...
    08e32510824f閱讀 131評(píng)論 0 0

友情鏈接更多精彩內(nèi)容