從宏觀的角度看 Gradle 的工做過程

本文默認讀者使用 macOShtml


以我本身爲例,剛開始接觸 Android 開發的時候,只是對 Java 開發有一些瞭解,對於 Android 開發的整個生態和技術棧只有一個模糊的認知。 缺少對 Gradle 的認知,在初級開發階段可能勉強能夠應付,只須要知道如何添加第三方依賴,如何調整 android block 中的配置基本上就夠了,可是隨着項目結構愈來愈複雜,默認提供的構建過程漸漸不能知足開發的需求,此時就要求開發者對 Gradle 的構建過程和原理有更深刻的瞭解,便於自定義個性化的構建過程。java

在閱讀完 《Gradle In Action》後,我發現 Android 工程的構建過程彷佛再也不那麼神祕了。 本文但願能從宏觀的角度帶剛接觸 Android 開發不久的同窗認識一下 Gradle,若是對 Gradle 工做原理感興趣,但願可以更加深刻了解,建議閱讀 《Gradle In Action》 這本書。android

背景

隨着項目規模增大,軟件工程師須要考慮的事情會愈來愈多。成功構建並運行一個項目再也不像單文件的 HelloWorld 同樣簡單。隨着持續集成思想的普及,一次成功的構建可能分爲 checkStyle,Lint,編譯,單元測試,集成測試,代碼裁剪,代碼混淆,打包部署等多個步驟。若是項目中引用了第三方 lib,那麼第三方 lib 會有版本迭代,甚至多個第三方 lib 可能又依賴了不一樣版本的同一個第三方 lib,形成依賴版本衝突,事情會愈來愈複雜。咱們須要使每個 Commit 老是能構建出徹底相同的結果,Git 對於二進制文件的版本管理又不是那麼駕輕就熟,手動構建經常會引入人爲變數致使構建出錯。因此構建過程自動化迫在眉睫。git

常見的 Java 構建工具

  • Ant (Anothre Neat Tool) 2000年
    • 使用 XML 描述構建的步驟
    • 只負責構建步驟管理,若是要添加依賴管理的功能,還須要引入 Ivy
  • Maven 2004年
    • convention over configuration 的思想,無需配置或者僅需少許配置便可開始構建
    • 和 Ant 對比增長了依賴庫管理
  • Gradle 2007年
    • 使用 Groovy DSL 替代繁瑣的 XML
    • 支持增量構建
    • 項目結構更加靈活

Google 基於 Gradle 經過 Android Gradle Plugin 提供了自動化構建的工具,對開發者隱藏了大量的繁瑣的構建過程,暴露一些可被開發者配置的屬性,大大的簡化了 Android 項目管理的複雜度的同時又不失靈活性。web

在這裏列舉的構建工具不止能夠用來構建 Java 相關的項目。只要能表達出構建步驟,就可使用這些工具來進行項目構建。好比,你可使用 Gradle 來構建一個 iOS 的項目。api

Gradle Wrapper:

The Wrapper is a script that invokes a declared version of Gradle, downloading it beforehand if necessary. As a result, developers can get up and running with a Gradle project quickly without having to follow manual installation processes saving your company time and money.bash

docs.gradle.org/current/use…網絡

構建工具也是須要版本迭代的,一個大的版本迭代可能不會提供向前的兼容性,也就是說,在 A 機器上和 B 機器上裝了兩個不一樣版本的 Gradle,結果可能致使同一個項目,在 A 的機器上能夠成功構建,而在 B 的機器上會構建失敗。 爲了不這個問題,保證每一個 Commit 總能構建出徹底相同的結果。Gradle 提供了 Gradle Wrapper,經過 Wrapper 運行 Gradle Task 的時候,會先檢查 gradle-wrapper.properties 中指定的位置下,指定版本的 Gradle 是否安裝,若是已經安裝,則將該 Gradle Task 交給 Gradle 處理。若是沒有安裝,則先下載安裝指定版本的 Gradle,而後再將 Gradle Task 交給 Gradle 處理。 gradlew 是一個 script,是 Gradle Wrapper 的入口,Windows 下是 gradlew.bat。 gradle-wrapper.jar 提供了 Gradlew Wrapper 的核心功能。oracle

目錄結構以下圖: app

圖片

以下圖所示是一個典型的使用 Gradle 進行構建的 Android 工程。 工程中包含兩個 Project:

  1. TutorialAndroid -- RootProject
  2. app -- SubProject

圖片
可使用以下命令查看工程中的 Project

gradlew projects
複製代碼

gradlew 是入口 Script, projects 其實是 Gradle 一個內置的 Task。 關於 Task 的概念,下面再解釋。 運行上面的命令,結果以下圖所示,能夠看到,通常咱們開發時修改 **app **只是一個子項目,RootProject 其實是 app 的上級目錄中的 TutorialAndroid。

圖片

構建過程

Gradle 的構建過程分爲如下幾個階段: initialization -> configuration -> execution

  1. initialization phase
    • Gradle 使用 Project 對象來表示項目,在 initialization 階段,Gradle 會爲每一個參與本次構建的項目建立一個 Project 對象。
    • 由於 Gradle 支持多項目構建,因此在初始化階段的時候,須要判斷哪些項目須要參與本次構建。
    • Gradle 能夠從 Project 的根目錄開始構建,也能夠從任意包含 build file 的子文件架開始構建。不管從哪裏開始構建,Gradle 都須要知道有哪些 Project 須要參與構建,Root Project 的 settings.gradle 中聲明瞭須要參與構建的 Project 的信息。因此 Gradle 在這個階段作的事情,就是從當前目錄開始,逐級向上搜索 settings.gradle ,若是找到了,就按照 settings.gradle 中聲明的信息設置本次構建,若是最終沒有找到,那麼就默認只有當前所在的 Project 須要參與本次構建。
  2. configuration phase

    A Task represents a single atomic piece of work for a build, such as compiling classes or generating javadoc.

    A Task is made up of a sequence of Action objects. When the task is executed, each of the actions is executed in turn, by calling Action.execute(T). You can add actions to a task by calling Task.doFirst(org.gradle.api.Action) or Task.doLast(org.gradle.api.Action).

    docs.gradle.org/current/dsl…

    • Task 屬於 Project 對象。能夠在 build.gradle 文件中簡單定義 Task
    // 定義好 Task 以後,就能夠經過 `gradlew simpleTask` 來運行指定的 Task
     task simpleTask {
         doLast {
         println "This is a simple task."
         }
     }
    複製代碼
    • 項目構建過程分爲不少步驟,在 Gradle 中用 Task 來表示這些步驟,Task 之間可能有依賴關係,例如:必須先執行完 compile Task,才能執行 unitTest Task。在 configuration 階段,Gradle 會分析 Task 之間的依賴關係,配置初始化階段建立的 Project 對象。

    Gradle determines the subset of the tasks, created and configured during the configuration phase, to be executed. The subset is determined by the task name arguments passed to the gradle command and the current directory.

    docs.gradle.org/current/use…

    • 當一個 Project 的 Task 愈來愈複雜,或者多個項目都須要共用同一個 Task 的時候,爲了提升代碼複用性,能夠編寫 Plugin 將建立 Task 等邏輯封裝起來。

      圖片
      build.gradle 中,如圖所示就是在使用封裝好的 Plugin。

    • 提升了代碼複用性的同時,還須要提供足夠的靈活性。Plugin 能夠經過 Extension 暴露一些可配置的屬性。這裏先不講,超綱了。

  3. execution phase
    • 根據上一步計算出的任務執行順序去執行須要執行的 Tasks。

以上就是 Gradle 的工做過程。

Tricks

  1. 使用 Proxy

    在國內特殊的網絡環境,能夠經過設置 Proxy 或 Repo Mirror 的方式來提升下載依賴的 Library 的速度。

    阿里提供的鏡像 maven.aliyun.com/mvn/view Gradle 使用 Java 的 Networking Properties 讀取 Proxy 參數。可供設置的參數參考如下文檔。

  2. 注意你的電腦中運行了多少 Gradle Daemon

    Gradle 提供了 Daemon 機制來提升構建速度,可是 Gradle Daemon 的複用是有條件的。 若是恰巧給 Gradle Daemon 設置了一個比較大的 maximum heap size, 可能在開發的過程當中,多個 Daemon 會佔用過多的內存,影響電腦運行速度。除了前面給出的條件,還有兩點是以前開發過程當中遇到過的:

    • 沒有正確使用 Gradle 提供的 wrapper Task 去升級 Gradle 版本,致使在使用 Gradle Wrapper Script 運行任務時,判斷 Gradle 版本的函數不兼容,啓用多個 Daemon。這個問題的表現方式通常發現同一個版本的 Gradle 被 Gradle Wrapper 重複下載。
    • IDE 提供了 Build in JRE,致使在 IDE 中運行的 Gradle 和在 Terminal 下運行的 Gradle 雖然版本相同,可是沒法複用。能夠統一二者使用的 JRE。好比在 Android Studio 中打開 Project Structure,在 SDK Location Tab 下設置 JDK Location,使其和 Terminal 中使用的 Java 路徑統一。

參考資料

《Gradle In Action》 -- Benjamin Muschko

Gradle 最佳實踐

構建工具的進化:ant, maven, gradle

@Eric

相關文章
相關標籤/搜索