Gradle構建的生命週期和其物件的理解
文章主題內容來自 Gradle官方文件
的 Understanding the Build Lifecycler
章節。通讀完該章節,大大加深了我對task物件,project物件,gradle.build指令碼和project物件的關係等這3個概念的理解。
官方文件地址: ofollow,noindex">https://docs.gradle.org/current/userguide/build_lifecycle.html#sub:building_the_tree

構建的不同階段
一個 gradle
的構建有3個不同的階段
-
初始化(Initialization)
Gradle支援單和多project的構建。在初始化階段,gradle決定了哪一個或哪些project將要參與到這次構建,並且為每個project建立一個
Project
物件。(注意,一個project對應一個build.gradle檔案) -
安裝(Configuration)
在這個階段,
Project
物件被安裝(個人猜測是執行Project物件的建構函式)。所有參與到這次構建的build.gradle指令碼檔案都會被執行。 -
執行(Execution)
在此階段,gradle將會決定在安裝(Configuration)階段所建立和裝配的tasks的哪些子集tasks要被執行。被執行的那些task是通過
gradle
命令的引數中的task的名字和當前在哪個目錄下來決定的。gradle然後執行每一個被選中的task。
settings.gradle
- 除了
build.gradle
指令碼之外,gradle還定義了一個settings.gradle
檔案。這個檔案會在初始化(initialization)階段被執行。 - 一個multi-project的構建必須有一個
settings.gradle
檔案在根目錄。因為後者定義了哪些project(也就是build.gradle指令碼)會參與到這個構建。當然,單project(單個build.gradle指令碼)的情況下,settings.gradle
可有可無。
3個不同階段在構建中的執行順序
1. 定義了3個task來分別檢視他們執行時列印的情況
-
settings.gradle
檔案println('initialization:settings.gradle被執行')
-
build.gradle
檔案println('configuration:build.gradle') task configured{ println('configuration:task configured') } task A{ println('configuration:task A') doLast{ println '這裡執行task:A#doLast' } } task B { doFirst{ println '這裡執行task:B#doFirst' } doLast{ println '這裡執行task:B#doLast' } println 'configuration:task B' }
-
$ gradle configured
william@localhost:~/IdeaProjects/1$ gradle configured initialization:settings.gradle被執行 > Configure project : configuration:build.gradle configuration:task configured configuration:task A configuration:task B
-
$ gradle A
initialization:settings.gradle被執行 > Configure project : configuration:build.gradle configuration:task configured configuration:task A configuration:task B > Task :A 這裡執行task:A#doLast
-
$ gradle B
並被新增到Project物件的一個欄位TaskContainer tasks中,initialization:settings.gradle被執行 > Configure project : configuration:build.gradle configuration:task configured configuration:task A configuration:task B > Task :B 這裡執行task:B#doFirst 這裡執行task:B#doLast
-
$ gradle A B
initialization:settings.gradle被執行 > Configure project : configuration:build.gradle configuration:task configured configuration:task A configuration:task B > Task :A 這裡執行task:A#doLast > Task :B 這裡執行task:B#doFirst 這裡執行task:B#doLast
2. 結論
-
初始化階段執行settings.gradle指令碼中的內容,
-
安裝階段執行build.gradle指令碼中除了
task.doLast(Closure c)
和task.doFirst(Closure c)
中的所有內容。因為在安裝階段時,該build.gradle
檔案對應的Project
物件已經建立,此時安裝階段對應的就是Project
物件執行其構造方法。而上述程式碼中在build.gradle
中定義了3個task,那麼在安裝階段,這3個task就會被建立為3個task物件例項,並且被加入到Project
物件的TaskContainer
容器中,此後,這3個task例項就作為Project
物件的屬性,可以直接使用了。 -
執行階段,根據輸入的task的名稱和相關依賴(如果存在),去遍歷執行對應的task物件中
private List<ContextAwareTaskAction> actions=new ArrayList<>();
的所有的方法,而當我們呼叫
doLast
或者doFirst
都是往那個ArrayList
的頭尾插入由我們傳入的閉包而轉化成的Action
介面的物件。而Action
介面長這樣:@HasImplicitReceiver public interface Action<T> { void execute(T t); }
那麼到執行階段,這個task的
Action
的容器actions
就會被遍歷並呼叫每個Action
的execute(T t)
方法。
響應build.gradle指令碼的生命週期
1. Project的安裝
build.gradle中的內容依靠 Project
物件的構造方法來安裝 Project
物件,其中有兩個回撥暴露給我們,分別是
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') }
執行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
執行結果如上, Project#afterEvaluated(Closure closure)
方法會在 Project
物件全部安裝完之後被呼叫,這也是其構造方法暴露給客戶端的回撥,那麼 Project
物件的構造方法的虛擬碼如下:
public ProjectImpl(){ runClosureBeforeEvalueation();//執行安裝前的閉包 configureCodeInSript();//安裝 runClosureAfterEvalueation();//執行安裝後的閉包 }
2. Task的建立
在一個task物件被新增到一個 Project
物件後,可以立刻收到一個回撥。
用 tasks.whenTaskAdded(Closure closure)
就可以辦到
build.gradle:
tasks.whenTaskAdded { println(it.name) } task A { println('configure A') } task B { println 'configure B' }
執行task A
william@localhost:~/IdeaProjects/1$ gradle A > Configure project : A configure A B configure B
3.Task的執行
gradle.taskGraph.addTaskExecutionListener(new TaskExecutionListener() { @Override void beforeExecute(Task task) { } @Override void afterExecute(Task task, TaskState state) { } }) gradle.taskGraph.beforeTask { } gradle.taskGraph.afterTask { }
上面的介面的方法和下面的兩個閉包方法的作用都是一樣的,我認為他們相比於 Task#doFirst
或者 Task#doLast
方法的優點在於, TaskExecutionGraph
的這幾個方法,是為每一個安裝了的task物件都插入一段回撥,這段回撥在每一個task執行前後被呼叫。
個人總結:
- 在執行build.gradle之前,他所對應的
Project
物件已然建立,猜測是在執行settings.gradle
時已經建立了。 - build.gradle中的所有程式碼,都作為
Project
物件的建構函式一部分而插入建構函式。 - 在build.gradle指令碼中建立的所有Task物件都在建立後都被
Project
物件的TaskContainer
這個容器物件引用。 - 命令列每執行一次gradle命令,就建立一個程式,從類似java的
main
方法開始執行。建立Project
物件,執行build.gradle指令碼來安裝(在他的構造方法中),而gradle命令後面跟隨的是task的名字,此時裝配好Project
物件後,根據傳入的task的名字來去直接執行這個task。當task執行完畢輸出結果後,程式結束,main()
方法結束。 - 當直接執行gradle命令而不加task,也會依然會執行build.gradle中為
Project
裝配而存在的程式碼,即此時Project
物件存在,但是隻是不去execute task了。