前言
成爲一名優秀的Android開發,需要一份完備的知識體系,在這裏,讓我們一起成長爲自己所想的那樣~。
目前,Gradle 自動化技術越來越重要,也有許多同學已經能夠製作出自己的 Gradle 插件,但是一直有一些 “梗” 遺留在我們腦海中,無時無刻不提醒着我們,你真的掌握了嗎?例如,“梗1”:Gradle 插件的整體實現架構?我:...,“梗2”:Android Gradle 插件更新歷史有哪些重要優化或者改進?我:..., “梗3”:Gradle 構建的核心流程是怎樣的?我:...,“梗4”:Gradle 中依賴實現的原理?我:..., “梗5”:AppPlugin 構建流程?我:..., “梗6”:assembleDebug 打包流程?我:...., “梗7”:一些很重要 Task 實現原理能否說說?我:... 。是否有很多點並沒有真正地去了解過呢?
思維導圖
目錄
- 一、Gradle 插件實現架構概述
- 二、瞭解 Android Gradle Plugin 的更新歷史
- 1、Android Gradle Plugin V3.5.0(2019 年 8 月)
- 2、Android Gradle Plugin V3.4.0(2019 年 4 月)
- 3、Android Gradle Plugin V3.3.0(2019 年 1 月)
- 4、Android Gradle Plugin V3.2.0(2018 年 9 月)
- 5、Android Gradle Plugin V3.1.0(2018 年 3 月)
- 6、Android Gradle Plugin V3.0.0(2017 年 10 月)
- 7、Android Gradle Plugin V2.3.0(2017 年 2 月)
- 三、Gradle 構建核心流程解析
- 1、LoadSettings
- 2、Configure
- 3、TaskGraph
- 4、RunTasks
- 5、Finished
- 四、關於 Gradle 中依賴實現的原理
- 1、通過 MethodMissing 機制,間接地調用 DefaultDependencyHandler 的 add 方法去添加依賴。
- 2、不同的依賴聲明,其實是由不同的轉換器進行轉換的。
- 3、Project 依賴的本質是 Artifacts 依賴,也即 產物依賴。
- 4、什麼是 Gradle 中的 Configuration?
- 5、Task 是如何發佈自己 Artifacts 的?
- 五、AppPlugin 構建流程
- 1、準備工作
- 2、configureProject 配置項目
- 3、configureExtension 配置 Extension
- 4、TaskManager#createTasksBeforeEvaluate 創建不依賴 flavor 的 task
- 5、BasePlugin#createAndroidTasks 創建構建 task
- 六、assembleDebug 打包流程淺析
- 1、Android 打包流程回顧
- 2、assmableDebug 打包流程淺析
- 七、重要 Task 實現源碼分析
- 1、資源處理相關 Task
- 2、將 Class 文件打包成 Dex 文件的過程
- 八、總結
- 最後的最後
一、Gradle 插件實現架構概述
Android Gradle plugin 團隊在 Android Gradle V3.2.0 之前一直是都是用 Java 編寫的 Gradle 插件,在 V3.2.0 便採用了 Kotlin 進行大面積的重寫。儘管 Groovy 語法簡潔,且其閉包的寫法非常靈活,但是 Android Studio 對 Groovy 的支持非常不友好,因此,目前寫自定義的 Gradle 插件時我們還是儘量使用 Kotlin,這樣能儘量避免編寫插件時提示不夠造成的坑。
下面,我們就來看看 Gradle 插件的整體實現架構,如下圖所示:
在最下層的是底層 Gradle 框架,它主要提供一些基礎的服務,如 task 的依賴,有向無環圖的構建等等。
上面的則是 Google 編譯工具團隊的 Android Gradle plugin
框架,它主要是 在 Gradle 框架的基礎上,創建了很多與 Android 項目打包有關的 task 及 artifacts(每一個 task 執行完成之後通常都會輸出產物)。
最上面的則是開發者自定義的 Plugin,關於自定義 Plugin 通常有兩種使用套路,如下所示:
- 1)、在 Android Gradle plugin 提供的 task 的基礎上,插入一些自定義的 task。
- 2)、增加 Transform 進行編譯時代碼注入。
二、瞭解 Android Gradle Plugin 的更新歷史
下表列出了各個 Android Gradle 插件版本所需的 Gradle 版本。我們應該使用 Android Gradle Plugin 與 Gradle 這兩者的最新版本以獲得最佳的性能。
插件版本 | 所需的 Gradle 版本 |
---|---|
1.0.0 - 1.1.3 | 2.2.1 - 2.3 |
1.2.0 - 1.3.1 | 2.2.1 - 2.9 |
1.5.0 | 2.2.1 - 2.13 |
2.0.0 - 2.1.2 | 2.10 - 2.13 |
2.1.3 - 2.2.3 | 2.14.1+ |
2.3.0+ | 3.3+ |
3.0.0+ | 4.1+ |
3.1.0+ | 4.4+ |
3.2.0 - 3.2.1 | 4.6+ |
3.3.0 - 3.3.2 | 4.10.1+ |
3.4.0 - 3.4.1 | 5.1.1+ |
3.5.0+ | 5.4.1-5.6.4 |
目前最新的 Android Gradle Plugin 版本爲 V3.6.2,Gradle 版本爲 V5.6.4。下面,我們瞭解下 Android Gradle Plugin 更新歷史中比較重要的更新變化。
1、Android Gradle Plugin V3.5.0(2019 年 8 月)
本次更新的重中之重是 提高項目的構建速度。
2、Android Gradle Plugin V3.4.0(2019 年 4 月)
如果使用的是 Gradle 5.0 及更高版本,默認的 Gradle 守護進程內存堆大小會從 1 GB 降到 512 MB。這可能會導致構建性能降低。如果要替換此默認設置,請在項目的 gradle.properties 文件中指定 Gradle 守護進程堆大小。
1)、新的 Lint 檢查依賴項配置
增加了新的依賴項配置 lintPublish,並更改了原有 lintChecks 的行爲,它們的作用分別如下所示:
lintChecks
:僅用於在本地構建項目時運行的 Lint 檢查。lintPublish
:在已發佈的 AAR 中啓用 Lint 檢查,這樣使用此 AAR 的項目也會應用那些 Lint 檢查。
其示例代碼如下所示:
dependencies {
// Executes lint checks from the ':lint' project at build time.
lintChecks project(':lint')
// Packages lint checks from the ':lintpublish' in the published AAR.
lintPublish project(':lintpublish')
}
複製代碼
2)、Android 免安裝應用功能插件棄用警告
在之前的版本可以使用 com.android.feature
插件構建免安裝應用,現在建議使用動態功能插件,這樣便可以通過單個 Android App Bundle 發佈安裝版應用和免安裝應用。
3)、R8 默認處於啓用狀態
R8 將 desugar(脫糖:將 .class 字節碼轉換爲 .dex 字節碼的過程)、壓縮、混淆、優化和 dex 處理整合到了一個步驟中,從而顯著提升了構建性能。R8 是在 Android Gradle Plugin V3.2.0 中引入的,對於使用插件 V3.4.0 及更高版本的應用和 Android 庫項目來說,R8 已經默認處於啓用狀態。
R8 引入之前的編譯流程
R8 引入之後的編譯流程
可以看到,R8 組合了 Proguard、D8 的功能。如果遇到因 R8 導致的編譯失敗的問題,可以配置以下代碼停用 R8:
# Disables R8 for Android Library modules only.
android.enableR8.libraries = false
# Disables R8 for all modules.
android.enableR8 = false
複製代碼
3)、棄用 ndkCompile
此時使用 ndkBuild 編譯原生庫會收到構建錯誤。我們應該 使用 CMake 或 ndk-build 將 C 和 C++ 代碼添加到項目中。
3、Android Gradle Plugin V3.3.0(2019 年 1 月)
1)、爲庫項目更快地生成 R 類
在老版本中,Android Gradle 插件會爲項目的每個依賴項生成一個 R.java 文件,然後將這些 R 類和應用的其他類一起編譯。現在,插件會直接生成包含應用的已編譯 R 類的 JAR,而不會先編譯中間的 R.java 類。這不僅可以顯著提升包含多個庫子項目和依賴項的項目的編譯性能,還可以加快在 Android Studio 中索引文件的速度。
4、Android Gradle Plugin V3.2.0(2018 年 9 月)
新的代碼壓縮器 R8
R8 是一種執行代碼壓縮和混淆的新工具,替代了 ProGuard。我們只需將以下代碼添加到項目的 gradle.properties 文件中,即可開始使用 R8 的預覽版本:
android.enableR8 = true
複製代碼
使用 D8 進行 desugar 的功能現已默認處於啓用狀態
5、Android Gradle Plugin V3.1.0(2018 年 3 月)
新的 DEX 編譯器 (D8)
默認情況下,Android Studio 此時會使用名爲 D8 的新 DEX 編譯器。DEX 編譯是指針對 ART (對於較早版本的 Android,則針對 Dalvik)將 .class 字節碼轉換爲 .dex 字節碼的過程。與之前的編譯器(DX)相比,D8 的編譯速度更快,輸出的 DEX 文件更小,同時卻能保持相同甚至更出色的應用運行時性能。
如果在使用 D8 的過程中出現了問題,可以在 gradle.properties 配置以下代碼暫時停用 D8 並使用 DX:
android.enableD8=false
複製代碼
6、Android Gradle Plugin V3.0.0(2017 年 10 月)
1)、通過精細控制的任務圖提升了多模塊項目的並行性。
更改依賴項時,Gradle 通過不重新編譯那些無法被訪問的依賴項 API 模塊來加快編譯速度。此時可以利用 Gradle 的新依賴項配置(implementation、api、compileOnly 和 runtimeOnly)限制哪些依賴項會將其 API 泄露給其他模塊。
2)、藉助每個類的 dex 處理,可加快增量編譯速度。
每個類現在都會編譯成單獨的 DEX 文件,並且只會對修改過的類重新進行 dex 處理。
3)、啓用 Gradle 編譯緩存優化某些任務來使用緩存的輸出
啓用 Gradle 編譯緩存能夠優化某些任務來使用緩存的輸出,從而加快編譯速度。
4)、AAPT2 默認已啓用並改進了增量資源處理
如果在使用 AAPT2 時遇到了問題,我們可以停用 AAPT2,在 gradle.properties 文件中設置如下代碼:
android.enableAapt2=false
複製代碼
然後,通過在命令行中運行 ./gradlew --stop 來重啓 Gradle 守護進程。
7、Android Gradle Plugin V2.3.0(2017 年 2 月)
1)、增加編譯緩存
存儲編譯項目時 Android 插件生成的特定輸出。使用緩存時,編譯插件的速度會明顯加快,因爲編譯系統在進行後續編譯時可以直接重用這些緩存文件,而不必重新創建。此外,我們也可以 使用 cleanBuildCache Task 去清除編譯緩存。
更老版本的的更新細節請查閱 gradle-plugin。
三、Gradle 構建核心流程解析
當我們輸入 ./gradlew/gradle ... 命令之後,一個 Gradle 構建就開始了。它包含如下 三個步驟
:
- 1)、首先,初始化 Gradle 構建框架自身。
- 2)、然後,把命令行參數包裝好送給 DefaultGradleLauncher。
- 3)、最後,觸發 DefaultGradleLauncher 中 Gradle 構建的生命週期,並開始執行標準的構建流程。
在開始深入源碼之前,我們可以先自頂向下瞭解下 Gradle 構建的核心流程圖,以便對 Gradle 的構建流程建立一個整體的感知。
當我們執行一個 gralde 命令時,便會調用 gradle/wrapper/gradle-wrapper.jar 裏面 org.gradle.wrapper.GradleWrapperMain 類的 main 方法,它就是 gradle 的一個入口方法。該方法的核心源碼如下所示:
public static void main(String[] args) throws Exception {
...
// 1、索引到 gradle-wrapper.properties 文件中配置的 gradle zip 地址,並由此包裝成一個 WrapperExecutor 實例。
WrapperExecutor wrapperExecutor = WrapperExecutor.forWrapperPropertiesFile(propertiesFile);
// 2、使用 wrapperExecutor 實例的 execute 方法執行 gradle 命令。
wrapperExecutor.execute(args, new Install(logger, new Download(logger, "gradlew", "0"), new PathAssembler(gradleUserHome)), new BootstrapMainStarter());
}
複製代碼
然後,我們繼續看看 wrapperExecutor 的 execute 方法,源碼如下所示:
public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception {
// 1、下載 gradle wrapper 需要的依賴與源碼。
File gradleHome = install.createDist(this.config);
// 2、從這裏開始執行 gradle 的構建流程。
bootstrapMainStarter.start(args, gradleHome);
}
複製代碼
首先,在註釋1處,會下載 gradle wrapper 需要的依賴與源碼。接着,在註釋2處,便會調用 bootstrapMainStarter 的 start 方法從這裏開始執行 gradle 的構建流程。其內部最終會依次調用 DefaultGradleLauncher 的 getLoadedSettings、getConfiguredBuild、executeTasks 與 finishBuild 方法,而它們對應的狀態都定義在 DefaultGradleLauncher 中的 Stage 枚舉類中,如下所示:
private static enum Stage {
LoadSettings,
Configure,
TaskGraph,
RunTasks {
String getDisplayName() {
return "Build";
}
},
Finished;
private Stage() {
}
String getDisplayName() {
return this.name();
}
}
複製代碼
下面,我們就對這五個流程來進行詳細地分析。
1、LoadSettings
當調用 getLoadedSettings 方法時便開始了加載 Setting.gradle 的流程。其源碼如下所示:
public SettingsInternal getLoadedSettings() {
this.doBuildStages(DefaultGradleLauncher.Stage.LoadSettings);
return this.gradle.getSettings();
}
複製代碼
這裏又繼續調用了 doBuildStages 方法進行處理,內部實現如下所示:
private void doBuildStages(DefaultGradleLauncher.Stage upTo) {
Preconditions.checkArgument(upTo != DefaultGradleLauncher.Stage.Finished, "Stage.Finished is not supported by doBuildStages.");
try {
// 當 Stage 是 RunTask 的時候執行。
if (upTo == DefaultGradleLauncher.Stage.RunTasks && this.instantExecution.canExecuteInstantaneously()) {
this.doInstantExecution();
} else {
// 當 Stage 不是 RunTask 的時候執行。 this.doClassicBuildStages(upTo);
}
} catch (Throwable var3) {
this.finishBuild(upTo.getDisplayName(), var3);
}
}
複製代碼
繼續調用 doClassicBuildStages 方法,源碼如下所示:
private void doClassicBuildStages(DefaultGradleLauncher.Stage upTo) {
// 1、當 Stage 爲 LoadSettings 時執行 prepareSettings 方法去配置並生成 Setting 實例。
this.prepareSettings();
if (upTo != DefaultGradleLauncher.Stage.LoadSettings) {
// 2、當 Stage 爲 Configure 時執行 prepareProjects 方法去配置工程。
this.prepareProjects();
if (upTo != DefaultGradleLauncher.Stage.Configure) {
// 3、當 Stage 爲 TaskGraph 時執行 prepareTaskExecution 方法去構建 TaskGraph。
this.prepareTaskExecution();
if (upTo != DefaultGradleLauncher.Stage.TaskGraph) {
// 4、當 Stage 爲 RunTasks 時執行 saveTaskGraph 方法 與 runWork 方法保存 TaskGraph 並執行相應的 Tasks。 this.instantExecution.saveTaskGraph();
this.runWork();
}
}
}
}
複製代碼
可以看到,doClassicBuildStages
方法是個很重要的方法,它對所有的 Stage 任務進行了分發,這裏小結一下:
- 1)、當 Stage 爲 LoadSettings 時執行 prepareSettings 方法去配置並生成 Setting 實例。
- 2)、當 Stage 爲 Configure 時執行 prepareProjects 方法去配置工程。
- 3)、當 Stage 爲 TaskGraph 時執行 prepareTaskExecution 方法去構建 TaskGraph。
- 4)、當 Stage 爲 RunTasks 時執行 saveTaskGraph 方法 與 runWork 方法保存 TaskGraph 並執行相應的 Tasks。
然後,我們接着繼續看看 prepareSettings 方法,其源碼如下所示:
private void prepareSettings() {
if (this.stage == null) {
// 1、回調 BuildListener.buildStarted() 回調接口。
this.buildListener.buildStarted(this.gradle);
// 2、調用 settingPreparer 接口的實現類 DefaultSettingsPreparer 的 prepareSettings 方法。
this.settingsPreparer.prepareSettings(this.gradle);
this.stage = DefaultGradleLauncher.Stage.LoadSettings;
}
}
複製代碼
在 prepareSettings
方法做了兩件事:
- 1)、回調 BuildListener.buildStarted 接口。
- 2)、調用 settingPreparer 接口的實現類 DefaultSettingsPreparer 的 prepareSettings 方法。
我們繼續看到 DefaultSettingsPreparer 的 prepareSettings 方法,如下所示:
public void prepareSettings(GradleInternal gradle) {
// 1、執行 init.gradle,它會在每個項目 build 之前被調用,用於做一些初始化的操作。
this.initScriptHandler.executeScripts(gradle);
SettingsLoader settingsLoader = gradle.getParent() != null ? this.settingsLoaderFactory.forNestedBuild() : this.settingsLoaderFactory.forTopLevelBuild();
// 2、調用 SettingLoader 接口的實現類 DefaultSettingsLoader 的 findAndLoadSettings 找到 Settings.gradle 文件的位置。
settingsLoader.findAndLoadSettings(gradle);
}
複製代碼
在 prepareSettings
方法中做了兩項處理:
- 1)、執行 init.gradle,它會在每個項目 build 之前被調用,用於做一些初始化的操作。
- 2)、調用 SettingLoader 接口的實現類 DefaultSettingsLoader 的 findAndLoadSettings 找到 Settings.gradle 文件的位置。
DefaultSettingLoader 的 findAndLoadSettings 方法關聯的實現代碼非常多,限於篇幅,我這裏直接點出 findAndLoadSettings 方法中的主要處理流程:
- 1)、首先,查找 settings.gradle 位置。
- 2)、然後,編譯 buildSrc(Android 默認的 Plugin 目錄)文件夾下的內容。
- 3)、接着,解析 gradle.properites 文件:這裏會讀取 gradle.properties 文件裏的配置信息與命令行傳入的配置屬性並存儲。
- 4)、然後,解析 settings.gradle 文件:這裏最後會調用 BuildOperationScriptPlugin.apply 去執行 settings.gradle 文件。
- 5)、最後,根據 Settings.gradle 文件中獲得的信息去創建 project 以及 subproject 實例。
2、Configure
當執行完 LoadSetting 階段之後,就會執行 Configure 階段,而配置階段所作的事情就是 把 gradle 腳本編譯成 class 文件並執行。由前可知,此時會執行 prepareProjects 方法,如下所示:
private void prepareProjects() {
if (this.stage == DefaultGradleLauncher.Stage.LoadSettings) {
// 1、調用 ProjectsPreparer 接口的實現類 DefaultProjectsPreparer 的 prepareProjects 方法。
this.projectsPreparer.prepareProjects(this.gradle);
this.stage = DefaultGradleLauncher.Stage.Configure;
}
}
複製代碼
這裏會繼續調用 ProjectsPreparer 接口的實現類 DefaultProjectsPreparer 的 prepareProjects 方法。其源碼如下所示:
public void prepareProjects(GradleInternal gradle) {
...
// 1、如果在 gradle.properties 文件中指定了參數 configure-on-demand,則只會配置主項目以及執行 task 所需要的項目。
if (gradle.getStartParameter().isConfigureOnDemand()) {
this.projectConfigurer.configure(gradle.getRootProject());
} else {
// 2、如果沒有指定在 gradle.properties 文件中指定參數 configure-on-demand,則會調用 ProjectConfigurer 接口的實現類 TaskPathProjectEvaluator 的 configureHierarchy 方法去配置所有項目。
this.projectConfigurer.configureHierarchy(gradle.getRootProject());
(new ProjectsEvaluatedNotifier(this.buildOperationExecutor)).notify(gradle);
}
this.modelConfigurationListener.onConfigure(gradle);
}
複製代碼
在註釋1處,如果在 gradle.properties 文件中指定了參數 configure-on-demand,則只會配置主項目以及執行 task 所需要的項目。我們這裏只看默認沒有指定的情況。否則,在註釋2處,則會調用 ProjectConfigurer 接口的實現類 TaskPathProjectEvaluator 的 configureHierarchy 方法去配置所有項目。
我們接着繼續看到 configureHierarchy 方法,如下所示:
public void configureHierarchy(ProjectInternal project) {
this.configure(project);
Iterator var2 = project.getSubprojects().iterator();
while(var2.hasNext()) {
Project sub = (Project)var2.next();
this.configure((ProjectInternal)sub);
}
}
複製代碼
可以看到在 configureHierarchy 方法中使用了 Iterator 遍歷並配置了所有 Project。而 configure 方法最終會調用到 EvaluateProject 類的 run 方法,如下所示:
public void run(final BuildOperationContext context) {
this.project.getMutationState().withMutableState(new Runnable() {
public void run() {
try {
EvaluateProject.this.state.toBeforeEvaluate();
// 1、 回調 ProjectEvaluationListener 的 beforeEvaluate 接口。
LifecycleProjectEvaluator.this.buildOperationExecutor.run(new LifecycleProjectEvaluator.NotifyBeforeEvaluate(EvaluateProject.this.project, EvaluateProject.this.state));
if (!EvaluateProject.this.state.hasFailure()) {
EvaluateProject.this.state.toEvaluate();
try {
// 2、在 evaluate 方法中會設置默認的 init、wrapper task 和 默認插件,然後便會編譯、執行 build.gradle 腳本
LifecycleProjectEvaluator.this.delegate.evaluate(EvaluateProject.this.project, EvaluateProject.this.state);
} catch (Exception var10) {
LifecycleProjectEvaluator.addConfigurationFailure(EvaluateProject.this.project, EvaluateProject.this.state, var10, context);
} finally {
EvaluateProject.this.state.toAfterEvaluate();
// 3、回調 ProjectEvaluationListener.afterEvaluate 接口。
LifecycleProjectEvaluator.this.buildOperationExecutor.run(new LifecycleProjectEvaluator.NotifyAfterEvaluate(EvaluateProject.this.project, EvaluateProject.this.state));
}
}
if (EvaluateProject.this.state.hasFailure()) {
EvaluateProject.this.state.rethrowFailure();
} else {
context.setResult(ConfigureProjectBuildOperationType.RESULT);
}
} finally {
EvaluateProject.this.state.configured();
}
}
});
}
複製代碼
在 EvaluateProject 的 run 方法中有如下 三個重要的處理:
- 1)、回調 ProjectEvaluationListener 的 beforeEvaluate 接口。
- 2)、在 evaluate 方法中會設置默認的 init、wrapper task 和 默認插件,然後便會編譯並執行 build.gradle 腳本。
- 3)、回調 ProjectEvaluationListener.afterEvaluate 接口。
3、TaskGraph
執行完 初始化階段 與 配置階段 之後,就會 調用到 DefaultGradleLauncher 的 prepareTaskExecution 方法去創建一個由 Tasks 組成的一個有向無環圖。該方法如下所示:
private void prepareTaskExecution() {
if (this.stage == DefaultGradleLauncher.Stage.Configure) {
// 1、調用 TaskExecutionPreparer 接口的實現類 BuildOperatingFiringTaskExecutionPreparer 的 prepareForTaskExecution 方法。
this.taskExecutionPreparer.prepareForTaskExecution(this.gradle);
this.stage = DefaultGradleLauncher.Stage.TaskGraph;
}
}
複製代碼
這裏繼續調用了 TaskExecutionPreparer 接口的實現類 BuildOperatingFiringTaskExecutionPreparer 的 prepareForTaskExecution 方法,如下所示:
public void prepareForTaskExecution(GradleInternal gradle) {
this.buildOperationExecutor.run(new BuildOperatingFiringTaskExecutionPreparer.CalculateTaskGraph(gradle));
}
複製代碼
可以看到,這裏使用 buildOperationExecutor 實例執行了 CalculateTaskGraph 這個構建操作,我們看到它的 run 方法,如下所示:
public void run(BuildOperationContext buildOperationContext) {
// 1、填充任務圖
final TaskExecutionGraphInternal taskGraph = this.populateTaskGraph();
buildOperationContext.setResult(new Result() {
getRequestedTaskPaths() {
return this.toTaskPaths(taskGraph.getRequestedTasks());
}
public List<String> getExcludedTaskPaths() {
return this.toTaskPaths(taskGraph.getFilteredTasks());
}
private List<String> toTaskPaths(Set<Task> tasks) {
return ImmutableSortedSet.copyOf(Collections2.transform(tasks, new Function<Task, String>() {
public String apply(Task task) {
return task.getPath();
}
})).asList();
}
});
}
複製代碼
在註釋1處,直接調用了 populateTaskGraph 填充了 Tasks 有向無環圖。源碼如下所示:
TaskExecutionGraphInternal populateTaskGraph() {
// 1、這裏又調用了 TaskExecutionPreparer 接口的另一個實現類 DefaultTaskExecutionPreparer 的 prepareForTaskExecution 方法。
BuildOperatingFiringTaskExecutionPreparer.this.delegate.prepareForTaskExecution(this.gradle);
return this.gradle.getTaskGraph();
}
複製代碼
可以看到,在註釋1處,又調用了 TaskExecutionPreparer 接口的另一個實現類 DefaultTaskExecutionPreparer 的 prepareForTaskExecution 方法。該方法如下所示:
public void prepareForTaskExecution(GradleInternal gradle) {
// 1
this.buildConfigurationActionExecuter.select(gradle);
TaskExecutionGraphInternal taskGraph = gradle.getTaskGraph();
// 2、根據 Tasks 與 Tasks 間的依賴信息填充 taskGraph 實例。
taskGraph.populate();
this.includedBuildControllers.populateTaskGraphs();
if (gradle.getStartParameter().isConfigureOnDemand()) {
(new ProjectsEvaluatedNotifier(this.buildOperationExecutor)).notify(gradle);
}
}
複製代碼
在註釋1處,調用了 buildConfigurationActionExecuter 接口的 select 方法,這裏採用了 策略 + 接口隔離 設計模式,後續會依次調用 ExcludedTaskFilteringBuildConfigurationAction 的 configure 方法、 DefaultTasksBuildExecutionAction 的 configure 方法、TaskNameResolvingBuildConfigurationAction 的 configure 方法。
下面,我們依次來分析下其中的處理。
1)、ExcludedTaskFilteringBuildConfigurationAction#configure:處理需要排除的 task
public void configure(BuildExecutionContext context) {
// 1、獲取被排除的 Tasks 名稱,並依次把它們放入 filters 中。
GradleInternal gradle = context.getGradle();
Set<String> excludedTaskNames = gradle.getStartParameter().getExcludedTaskNames();
if (!excludedTaskNames.isEmpty()) {
Set<Spec<Task>> filters = new HashSet();
Iterator var5 = excludedTaskNames.iterator();
while(var5.hasNext()) {
String taskName = (String)var5.next();
filters.add(this.taskSelector.getFilter(taskName));
}
// 2、給 TaskGraph 實例添加要 filter 的 Tasks
gradle.getTaskGraph().useFilter(Specs.intersect(filters));
}
context.proceed();
}
複製代碼
在註釋1處,先獲取了被排除的 Tasks 名稱,並依次把它們放入 filters 中,接着在註釋2處給 TaskGraph 實例添加了要 filter 的 Tasks。
可以看到,這裏給 TaskGraph 設置了 filter 去處理需要排除的 task,以便在後面計算依賴的時候排除相應的 task。
2)、DefaultTasksBuildExecutionAction#configure:添加默認的 task
public void configure(BuildExecutionContext context) {
StartParameter startParameter = context.getGradle().getStartParameter();
Iterator var3 = startParameter.getTaskRequests().iterator();
TaskExecutionRequest request;
// 1、如果指定了要執行的 Task,則什麼也不做。
do {
if (!var3.hasNext()) {
ProjectInternal project = context.getGradle().getDefaultProject();
this.projectConfigurer.configure(project);
List<String> defaultTasks = project.getDefaultTasks();
// 2、如果沒有指定 DefaultTasks,則輸出 gradle help 信息。
if (defaultTasks.size() == 0) {
defaultTasks = Collections.singletonList("help");
LOGGER.info("No tasks specified. Using default task {}", GUtil.toString(defaultTasks));
} else {
LOGGER.info("No tasks specified. Using project default tasks {}", GUtil.toString(defaultTasks));
}
// 3、否則,添加默認的 Task。
startParameter.setTaskNames(defaultTasks);
context.proceed();
return;
}
request = (TaskExecutionRequest)var3.next();
} while(request.getArgs().isEmpty());
context.proceed();
}
複製代碼
可以看到,DefaultTasksBuildExecutionAction 類的 configure 方法的處理分爲如下三步:
- 1、如果指定了要執行的 Task,則什麼也不做。
- 2、如果沒有指定 defaultTasks,則輸出 gradle help 信息。
- 3、否則,添加默認的 defaultTasks。
3)、TaskNameResolvingBuildConfigurationAction#configure:計算 task 依賴圖
public void configure(BuildExecutionContext context) {
GradleInternal gradle = context.getGradle();
TaskExecutionGraphInternal taskGraph = gradle.getTaskGraph();
List<TaskExecutionRequest> taskParameters = gradle.getStartParameter().getTaskRequests();
Iterator var5 = taskParameters.iterator();
while(var5.hasNext()) {
TaskExecutionRequest taskParameter = (TaskExecutionRequest)var5.next();
// 1、解析 Tasks。
List<TaskSelection> taskSelections = this.commandLineTaskParser.parseTasks(taskParameter);
Iterator var8 = taskSelections.iterator();
// 2、遍歷並添加所有已選擇的 Tasks 至 taskGraph 實例之中。
while(var8.hasNext()) {
TaskSelection taskSelection = (TaskSelection)var8.next();
LOGGER.info("Selected primary task '{}' from project {}", taskSelection.getTaskName(), taskSelection.getProjectPath());
taskGraph.addEntryTasks(taskSelection.getTasks());
}
}
context.proceed();
}
複製代碼
這裏主要做了 兩個處理
:
- 1)、
解析 Tasks
:分析命令行中的 Task 的參數前面是否指定了 Project,例如 ':project:assembleRelease',如果沒有指定則選中 Project 下所有符合該 taskName 的 Task。 - 2)、
遍歷並添加所有已選擇的 Tasks 至 taskGraph 實例之中
: 在內部會處理 dependson finalizedby mustrunafter shouldrunafter 等 Tasks 間的依賴關係,並會把信息保存在 TaskInfo 之中。
4)、填充 task 依賴圖
最後,我們再回到 DefaultTaskExecutionPreparer 的 prepareForTaskExecution 方法,在註釋2處,我們就可以 調用 taskGraph 的 populate 方法去根據這些 Tasks 與 Tasks 之間的依賴信息去填充 taskGraph 實例了。
4、RunTasks
填充完 taskGraph 之後,我們就可以開始來執行這些 Tasks 了,我們看到 DefaultGradleLauncher 實例的 executeTasks 方法,如下所示:
public GradleInternal executeTasks() {
this.doBuildStages(DefaultGradleLauncher.Stage.RunTasks);
return this.gradle;
}
複製代碼
在 doBuildStages 方法中又會調用 doClassicBuildStages 方法,源碼如下所示:
private void doClassicBuildStages(DefaultGradleLauncher.Stage upTo) {
this.prepareSettings();
if (upTo != DefaultGradleLauncher.Stage.LoadSettings) {
this.prepareProjects();
if (upTo != DefaultGradleLauncher.Stage.Configure) {
this.prepareTaskExecution();
if (upTo != DefaultGradleLauncher.Stage.TaskGraph) {
this.instantExecution.saveTaskGraph();
// 1
this.runWork();
}
}
}
}
複製代碼
在註釋1處,繼續調用了 runWork 方法,如下所示:
private void runWork() {
if (this.stage != DefaultGradleLauncher.Stage.TaskGraph) {
throw new IllegalStateException("Cannot execute tasks: current stage = " + this.stage);
} else {
List<Throwable> taskFailures = new ArrayList();
// 1
this.buildExecuter.execute(this.gradle, taskFailures);
if (!taskFailures.isEmpty()) {
throw new MultipleBuildFailures(taskFailures);
} else {
this.stage = DefaultGradleLauncher.Stage.RunTasks;
}
}
}
複製代碼
這裏又繼續 調用了 buildExecuter 接口的實現類 DefaultBuildWorkExecutor 的 execute 方法,其實現如下所示:
public void execute(GradleInternal gradle, Collection<? super Throwable> failures) {
this.execute(gradle, 0, failures);
}
private void execute(final GradleInternal gradle, final int index, final Collection<? super Throwable> taskFailures) {
if (index < this.executionActions.size()) {
// 1
((BuildExecutionAction)this.executionActions.get(index)).execute(new BuildExecutionContext() {
public GradleInternal getGradle() {
return gradle;
}
public void proceed() {
// 2、執行完 executionActions 列表中的第一項後,便開始執行下一項。
DefaultBuildWorkExecutor.this.execute(gradle, index + 1, taskFailures);
}
}, taskFailures);
}
}
複製代碼
1)、執行 DryRunBuildExecutionAction:處理 DryRun
可以看到,這裏又調用了 BuildExecutionAction 接口的實現類 DryRunBuildExecutionAction 的 execute 方法,如下所示:
public void execute(BuildExecutionContext context, Collection<? super Throwable> taskFailures) {
GradleInternal gradle = context.getGradle();
// 1、如果命令行裏包含 --dry-run 參數,則會跳過該 Task 的執行,並輸出 Task 的名稱與執行的先後關係。
if (gradle.getStartParameter().isDryRun()) {
Iterator var4 = gradle.getTaskGraph().getAllTasks().iterator();
while(var4.hasNext()) {
Task task = (Task)var4.next();
this.textOutputFactory.create(DryRunBuildExecutionAction.class).append(((TaskInternal)task).getIdentityPath().getPath()).append(" ").style(Style.ProgressStatus).append("SKIPPED").println();
}
} else {
context.proceed();
}
}
複製代碼
在註釋1處,如果命令行裏包含了 --dry-run 參數,則會跳過該 Task 的執行,並輸出 Task 的名稱與執行的先後關係。
2)、執行:SelectedTaskExecutionAction:使用線程執行被選擇的 Tasks
執行完 DryRunBuildExecutionAction 後,我們再回到 DefaultBuildWorkExecutor 類的 execute 方法,在註釋2處,會執行 executionActions 列表中的下一項,即第二項:SelectedTaskExecutionAction,我們看看它的 execute 方法,如下所示:
public void execute(BuildExecutionContext context, Collection<? super Throwable> taskFailures) {
...
taskGraph.addTaskExecutionGraphListener(new SelectedTaskExecutionAction.BindAllReferencesOfProjectsToExecuteListener());
// 1、
taskGraph.execute(taskFailures);
}
複製代碼
可以看到,這裏直接調用了 TaskExecutionGraphInternal 接口的實現類 DefaultTaskExecutionGraph 的 execute 方法去執行 Tasks,其關鍵源碼如下所示:
public void execute(Collection<? super Throwable> failures) {
ProjectExecutionServiceRegistry projectExecutionServices = new ProjectExecutionServiceRegistry();
try {
// 使用線程池執行 Task
this.executeWithServices(projectExecutionServices, failures);
} finally {
projectExecutionServices.close();
}
}
複製代碼
這裏又繼續調用了 DefaultTaskExecutionGraph 的 executeWithServices 方法使用線程池並行執行 Task,其核心源碼如下所示:
private void executeWithServices(ProjectExecutionServiceRegistry projectExecutionServices, Collection<? super Throwable> failures) {
...
this.ensurePopulated();
...
try {
// 1
this.planExecutor.process(this.executionPlan, failures, new DefaultTaskExecutionGraph.BuildOperationAwareExecutionAction(this.buildOperationExecutor.getCurrentOperation(), new DefaultTaskExecutionGraph.InvokeNodeExecutorsAction(this.nodeExecutors, projectExecutionServices)));
LOGGER.debug("Timing: Executing the DAG took " + clock.getElapsed());
} finally {
...
}
}
複製代碼
最終,這裏是 調用了 PlanExecutor 接口的實現類 DefaultPlanExecutor 的 process 方法,如下所示:
public void process(ExecutionPlan executionPlan, Collection<? super Throwable> failures, Action<Node> nodeExecutor) {
ManagedExecutor executor = this.executorFactory.create("Execution worker for '" + executionPlan.getDisplayName() + "'");
try {
WorkerLease parentWorkerLease = this.workerLeaseService.getCurrentWorkerLease();
// 1、開始使用線程池異步執行 tasks
this.startAdditionalWorkers(executionPlan, nodeExecutor, executor, parentWorkerLease);
(new DefaultPlanExecutor.ExecutorWorker(executionPlan, nodeExecutor, parentWorkerLease, this.cancellationToken, this.coordinationService)).run();
this.awaitCompletion(executionPlan, failures);
} finally {
executor.stop();
}
}
複製代碼
在註釋1處,使用了線程池去異步執行 tasks,如下所示:
private void startAdditionalWorkers(ExecutionPlan executionPlan, Action<? super Node> nodeExecutor, Executor executor, WorkerLease parentWorkerLease) {
LOGGER.debug("Using {} parallel executor threads", this.executorCount);
for(int i = 1; i < this.executorCount; ++i) {
executor.execute(new DefaultPlanExecutor.ExecutorWorker(executionPlan, nodeExecutor, parentWorkerLease, this.cancellationToken, this.coordinationService));
}
}
複製代碼
需要了解的是,用於執行 Task 的線程默認是 8 個線程。這裏使用線程池執行了 DefaultPlanExecutor 的 ExecutorWorker,在它的 run 方法中最終會調用到 DefaultBuildOperationExecutor 的 run 方法去執行 Task。但在 Task 執行前還需要做一些預處理。
3)、Task 執行前的一些預處理
在 DefaultBuildOperationExecutor 的 run 方法中只做了兩件事,這裏我們只需大致瞭解下即可,如下所示:
- 1、首先,回調 TaskExecutionListener#beforeExecute。
- 2、然後,鏈式執行一系列對 Task 的通用處理,其具體的處理如下所示:
- 1)、
CatchExceptionTaskExecuter#execute
:增加 try catch,避免執行過程中發生異常。 - 2)、
ExecuteAtMostOnceTaskExecuter#execute
:判斷 task 是否執行過。 - 3)、
SkipOnlyIfTaskExecuter#execute
:判斷 task 的 onlyif 條件是否滿足執行。 - 4)、
SkipTaskWithNoActionsExecuter#execute
:跳過沒有 action 的 task,如果沒有 action 說明 task 不需要執行。 - 5)、
ResolveTaskArtifactStateTaskExecuter#execute
:設置 artifact 的狀態。 - 6)、
SkipEmptySourceFilesTaskExecuter#execut
:跳過設置了 source file 且 source file 爲空的 task,如果 source file 爲空則說明 task 沒有需要處理的資源。 - 7)、
ValidatingTaskExecuter#execute
:確認 task 是否可以執行。 - 8)、
ResolveTaskOutputCachingStateExecuter#execute
:處理 task 輸出緩存。 - 9)、
SkipUpToDateTaskExecuter#execute
:跳過 update-to-date 的 task。 - 10)、
ExecuteActionsTaskExecuter#execute
:用來真正執行 Task 的 executer。
- 1)、
可以看到,在真正執行 Task 前需要經歷一些通用的 task 預處理,最後纔會調用 ExecuteActionsTaskExecuter 的 execute 方法去真正執行 Task。
4)、真正執行 Task
public TaskExecuterResult execute(final TaskInternal task, final TaskStateInternal state, final TaskExecutionContext context) {
final ExecuteActionsTaskExecuter.TaskExecution work = new ExecuteActionsTaskExecuter.TaskExecution(task, context, this.executionHistoryStore, this.fingerprinterRegistry, this.classLoaderHierarchyHasher);
// 使用 workExecutor 對象去真正執行 Task。
final CachingResult result = (CachingResult)this.workExecutor.execute(new AfterPreviousExecutionContext() {
public UnitOfWork getWork() {
return work;
}
public Optional<String> getRebuildReason() {
return context.getTaskExecutionMode().getRebuildReason();
}
public Optional<AfterPreviousExecutionState> getAfterPreviousExecutionState() {
return Optional.ofNullable(context.getAfterPreviousExecution());
}
});
...
}
複製代碼
可以看到,這裏使用了 workExecutor 對象去真正執行 Task,在執行時便會回調 ExecuteActionsTaskExecuter.TaskExecution 內部類的 execute 方法,其實現源碼如下所示:
public WorkResult execute(@Nullable InputChangesInternal inputChanges) {
this.task.getState().setExecuting(true);
WorkResult var2;
try {
ExecuteActionsTaskExecuter.LOGGER.debug("Executing actions for {}.", this.task);
// 1、回調 TaskActionListener 的 beforeActions 接口。
ExecuteActionsTaskExecuter.this.actionListener.beforeActions(this.task);
// 2、內部會遍歷執行所有的 Action。
ExecuteActionsTaskExecuter.this.executeActions(this.task, inputChanges);
var2 = this.task.getState().getDidWork() ? WorkResult.DID_WORK : WorkResult.DID_NO_WORK;
} finally {
this.task.getState().setExecuting(false);
// 3、回調 TaskActionListener.afterActions。
ExecuteActionsTaskExecuter.this.actionListener.afterActions(this.task);
}
return var2;
}
複製代碼
在 ExecuteActionsTaskExecuter.TaskExecution 內部類的 execute 方法中做了三件事,如下所示:
- 1)、回調 TaskActionListener 的 beforeActions。
- 2)、內部會遍歷執行 Task 所有的 Action。 => 執行 Task 就是執行其中包含的所有 Action,這便是 Task 的本質。
- 3)、回調 TaskActionListener.afterActions。
當執行完 Task 的所有 Action 之後,便會最終在 DefaultBuildOperationExecutor 的 run 方法中回調 TaskExecutionListener.afterExecute 來標識 Task 最終執行完成。
5、Finished
在 Finished 階段僅僅只幹了一件比較重要的事情,就是 回調 buildListener 的 buildFinished 接口,如下所示:
private void finishBuild(String action, @Nullable Throwable stageFailure) {
if (this.stage != DefaultGradleLauncher.Stage.Finished) {
...
try {
this.buildListener.buildFinished(buildResult);
} catch (Throwable var7) {
failures.add(var7);
}
this.stage = DefaultGradleLauncher.Stage.Finished;
...
}
}
複製代碼
6、小結
從對 Gradle 執行 Task 的分析中可以看到 Task 的本質,其實就是一系列的 Actions。深入瞭解了 Gradle 的構建流程之後,我們再重新迴歸頭來看看開始的那一張 Gradle 的構建流程圖,如下所示:
此外,zhangyi54 做的 Gradle原理動畫講解 將枯燥的原理流程視覺化了,值得推薦。需要注意區分的是,動畫講解的 Gradle 版本比較老,但是主要的原理還是一致的,可以放心觀看。
注意:build.gradle 腳本的 buildscript 會在腳本的其它內容之前執行。
四、關於 Gradle 中依賴實現的原理
1、通過 MethodMissing 機制,間接地調用 DefaultDependencyHandler 的 add 方法去添加依賴
我們都知道,DependencyHandler 是用來進行依賴聲明的一個類,但是在 DependencyHandler 並沒有發現 implementation(), api(), compile() 這些方法,這是怎麼回事呢?
其實這是 通過 MethodMissing 機制,間接地調用 DependencyHandler 的實現 DefaultDependencyHandler 的 add() 方法將依賴添加進去的。
那麼,methodMissing 又是什麼?
它是 Groovy 語言的一個重要特性, 這個特性允許在運行時 catch 對於未定義方法的調用。
而 Gradle 對這個特性進行了封裝,一個類要想使用這個特性,只要實現 MixIn 接口即可。代碼如下所示:
public interface MethodMixIn {
MethodAccess getAdditionalMethods();
}
複製代碼
2、不同的依賴聲明,其實是由不同的轉換器進行轉換的
例如如下兩個依賴:
- 1)、
DependencyStringNotationConverter
: 將類似於 'androidx.appcompat:appcompat:1.1.0' 的常規依賴聲明轉換爲依賴。 - 2)、
DependencyProjectNotationConverter
: 將類似於 'project(":mylib")' 的依賴聲明轉換爲依賴。
此外,除了 project 依賴,其它的依賴最終都會轉換爲 SelfResolvingDependency, 而這個依賴可以實現自我解析。
3、Project 依賴的本質是 Artifacts 依賴,也即 產物依賴。
4、什麼是 Gradle 中的 Configuration?
implementation 與 api。但是,承擔 moudle 或者 aar 依賴僅僅是 Configuration 表面的任務,依賴的實質就是獲取被依賴項構建過程中產生的 Artifacts。
而對於大部分的 Task 來說,執行完之後都會有產物輸出。而在各個 moudle 之間 Artifacts 的產生與消費則構成了一個完整的生產者-消費者模型。
5、Task 是如何發佈自己 Artifacts 的?
每一個 task 都會調用 VariantScopeImpl 中的 publishIntermediateArtifact 方法將自己的產物進行發佈,最後會調用到 DefaultVariant 的 artifact 方法對產物進行發佈。
公衆號
我的公衆號 JsonChao
開通啦,如果您想第一時間獲取最新文章和最新動態,歡迎掃描關注~
參考鏈接:
1、Android Gradle Plugin V3.6.2 源碼
2、Gradle V5.6.4 源碼
3、Android Plugin DSL Reference
7、連載 | 深入理解Gradle框架之一:Plugin, Extension, buildSrc
9、連載 | 深入理解gradle框架之三:artifacts的發佈
10、Android Gradle Plugin 源碼解析(上)
11、Android Gradle Plugin 源碼解析(下)
13、Gradle 庖丁解牛(構建生命週期核心委託對象創建源碼淺析)
Contanct Me
● 微信:
歡迎關注我的微信:
bcce5360
● 微信羣:
由於微信羣已超過 200 人,麻煩大家想進微信羣的朋友們,加我微信拉你進羣。
● QQ羣:
2千人QQ羣,Awesome-Android學習交流羣,QQ羣號:959936182, 歡迎大家加入~
About me
Email: [email protected]
Blog: jsonchao.github.io/
掘金: juejin.im/user/5a3ba9…