【靈魂七問】深度探索 Gradle 自動化構建技術(五、Gradle 插件架構實現原理剖析 — 上)...

前言

成爲一名優秀的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

可以看到,在真正執行 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 方法對產物進行發佈

深度探索 Gradle 自動化構建技術(五、Gradle 插件架構實現原理剖析)下

公衆號

我的公衆號 JsonChao 開通啦,如果您想第一時間獲取最新文章和最新動態,歡迎掃描關注~

參考鏈接:


1、Android Gradle Plugin V3.6.2 源碼

2、Gradle V5.6.4 源碼

3、Android Plugin DSL Reference

4、Gradle DSL Reference

5、designing-gradle-plugins

6、android-training => gradle

7、連載 | 深入理解Gradle框架之一:Plugin, Extension, buildSrc

8、連載 | 深入理解gradle框架之二:依賴實現分析

9、連載 | 深入理解gradle框架之三:artifacts的發佈

10、Android Gradle Plugin 源碼解析(上)

11、Android Gradle Plugin 源碼解析(下)

12、Gradle 庖丁解牛(構建源頭源碼淺析)

13、Gradle 庖丁解牛(構建生命週期核心委託對象創建源碼淺析)

Contanct Me

● 微信:

歡迎關注我的微信:bcce5360

● 微信羣:

由於微信羣已超過 200 人,麻煩大家想進微信羣的朋友們,加我微信拉你進羣。

● QQ羣:

2千人QQ羣,Awesome-Android學習交流羣,QQ羣號:959936182, 歡迎大家加入~

About me

很感謝您閱讀這篇文章,希望您能將它分享給您的朋友或技術羣,這對我意義重大。

希望我們能成爲朋友,在 Github掘金上一起分享知識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章