如今 Android 開發免不了要和 Gradle 打交道,全部的 Android 開發確定都知道這麼在 build.gradle
中添加依賴,或者添加配置批量打包,可是真正理解這些腳本的人恐怕不多。其實 Gradle 的 build.gradle
能夠說是一個代碼文件,熟悉 Java 的人理解起來很簡單的,之因此不肯意去涉及,主要感受沒有必要去研究。要能看懂 build.gradle
,除了要了解 Groovy 的語法,還要了解 Gradle 的構建流程,要研究仍是要花一些時間的,因此這篇文章可讓一個 Java 程序員在一個小時內看懂 Gradle 的腳本。java
Gradle 構建由 Project 和 Task 組成,Project 保存項目的屬性,例如 name,版本號,代碼文件位置。Task 也是 Project 的一部分,可是它是可執行的任務,咱們最常使用的 build
就是一個 Task,Task 能夠依賴於另一個 Task,一個 Task 在執行的時候,它依賴的 Task 會先執行。這樣,當咱們 build 的時候,這個 Task 可能依賴不少的 Task,好比代碼檢查、註解處理,這樣一層層的依賴,最終經過 build Task 所有執行。android
Gradle 和 Groovy 這兩個名字很容易讓人產生混淆,這裏先解釋一下,Groovy 是一門編程語言,和 Java 同樣。Gradle 和一個自動化構建工具,其餘知名的構建工具還有 Maven 和 Ant。什麼自動化構建工具?用 Android 來舉例,打包一個 Apk 文件要作不少工做,代碼預處理,lint代碼檢查、處理資源、編譯 Java 文件等等,使用自動化構建工具,一個命令就能夠生成 Apk 了。程序員
Gradle 的 DSL 目前支持兩種語言的格式,Groovy 和 Kotlin,Kotlin 格式的 DSL 是在 5.0 引入的,相比 Groovy,Kotlin 使用的人數更多,也更好理解,在這兒主要介紹 Groovy 格式的 DSL。apache
介紹一下什麼是 DSL,DSL 是 Domain Specific Language
的縮寫,既領域專用語言。Gradle 的 DSL 專門用於配置項目的構建,不能作其餘工做,而像 Java 、C/C++ 這些就屬於通用語言,能夠作任何工做。編程
咱們還要理解什麼是腳本文件。在寫代碼 Java 代碼時,程序是從 main()
函數開始執行的,只有在 main()
中調用的代碼纔會執行。可是腳本文件不同,只要在文件內寫的代碼都會執行,Groovy
是支持腳本文件的,咱們配置好 Groovy 的開發環境,新建一個文件 test.groovy
,輸入如下內容:api
String hello = "Hello World!" println(hello) println("The End")
而後運行:閉包
groovy test.groovy
輸出結果爲:app
Hello World! The End
雖然沒有 main
函數,可是裏面的代碼都執行了。很明顯,build.gradle
就是一個 Groovy 的腳本文件,裏面就是 Groovy 代碼,裏面添加的全部代碼都會運行,咱們能夠試驗如下,隨便打開一個 Gradle 格式的項目,在 build.gradle
最下面添加一些 Java 代碼:maven
String hello = "Hello World!" System.out.println(hello)
而後執行:編程語言
./gradlew -q # -q 是不輸出額外的信息
咱們會看到輸出了 Hellow World
,說明咱們添加的代碼被執行了,那麼爲何能夠在 build.gradle
裏面寫 Java 代碼呢?這是由於 Groovy 是支持 Java 的語法的,在 Groovy 文件寫 Java 代碼是徹底沒有問題的。
build.gradle
的執行方式如今總結一下,build.gradle
就是一個 Groovy 格式腳本文件,裏面是 Groovy 或者 Java 代碼,構建的時候會順序執行,可是打開 build.gradle
,可能仍是一頭霧水,一個個字符和大括號組成的東西究竟是什麼鬼?咱們來看一下最長使用的 dependencies
:
dependencies { // This dependency is found on compile classpath of this component and consumers. implementation 'com.google.guava:guava:26.0-jre' // Use JUnit test framework testImplementation 'junit:junit:4.12' }
implementation
也能夠這樣寫:
implementation('com.google.guava:guava:26.0-jre')
implementation
其實就是一個函數,在 Groovy 中,函數調用可使用空格加參數的形式調用,例如
void foo(String params1, int param2) { println("param1 = $params1, param2 = $param2") } foo "aaa", 2
implementation 'com.google.guava:guava:26.0-jre'
就是調用了 implementation
函數添加了一個依賴。以此類推,dependencies
也是一個函數,在 IDEA
中,咱們能夠直接 Ctrl
加鼠標左鍵點擊進去看它的聲明:
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware { // ... void dependencies(Closure configureClosure); // ... }
咱們看到 dependencies
是 Project
一個方法,爲何能夠在 build.gradle
調用 Project
的方法呢,官方文檔裏面有相關的介紹。一個 Gradle 項目通常有一個 settings.gradle
文件和一個 build.gradle
文件,settings.gradle
用來配置目錄結構,子工程就是在 settings.gradle
裏面配置,Project
和 build.gradle
是一一對應的關係,Gradle 的構建流程以下:
一、生成一個 Settings
對象,執行 settings.gradle
對這個對象進行配置
二、使用 Settings
對象生成工程結構,建立 Project
對象
三、對全部 Project
執行對應的 build.gradle
進行配置
build.gradle
就是對 Project
的操做,例如,在 build.gradle
中輸入如下代碼
println "project name is ${this.name}"
輸出結果爲: project name is java_demo
,java_demo
就是咱們的 project name,咱們能夠認爲對 this
的操做就是對 project
的操做。
Groovy 也是有語法糖的,類的屬性能夠直接使用名字,例如 Project
的有兩個函數:
Object getVersion(); void setVersion(Object version);
那麼這就說明 Project
有一個 version
屬性,在 build.gradle
中咱們能夠這樣來使用:
version = "1.0" // 賦值,調用 setVersion() println version // 讀取,調用 getVersion()
在 Project
中沒有 getter
方法的屬性是不能賦值的,例如 name
,咱們能夠輸出 name
的值,可是 name = "demo"
是錯誤的。
因此,在 build.gradle
中的代碼就是修改 Project
,方式就是修改屬性或者調用相關的方法,plugins
方法是添加插件,repositories
方法是添加代碼倉庫,
閉包能夠認爲是能夠執行的代碼塊,Groovy 中閉包的聲明和執行方式以下:
Closure closure = { String item -> println(item) } closure("Hello") // 執行
和 Lambda 表達式很像,可是 Groovy 的閉包能夠先聲明,而後設置代理來執行,例如咱們聲明一個閉包:
Closure closure = { sayHello() }
這個閉包裏面執行了 sayHello()
函數,可是咱們沒有在任何地方聲明這個函數,在 Java 中,這是個編譯錯誤,可是 Groovy 是容許的,完整的執行的例子以下:
Closure closure = { sayHello() } class Foo { void sayHello() { println("Hello!!!") } } def foo = new Foo() closure.delegate = foo closure()
輸出結果爲:
Hello!!!
咱們爲閉包設置了一個代理 delegate
,只要這個代理有 sayHello()
方法,代碼就能執行,這就是爲何咱們查看 Project
的源碼,裏面不少函數參數類型都是 Closure
,例如:
void repositories(Closure configureClosure); void dependencies(Closure configureClosure);
repositories
在 build.gradle
中是這樣調用的:
repositories { // Use jcenter for resolving your dependencies. // You can declare any Maven/Ivy/file repository here. jcenter() }
咱們經過 IDE 進入 jcenter()
的聲明,進入的是:
public interface RepositoryHandler extends ArtifactRepositoryContainer { // ... }
因爲沒看過源碼,我也只能猜,我猜 repositories
這個閉包的 delegate
是一個 RepositoryHandler
,經過執行 RepositoryHandler
的方法,爲工程添加 Repository
來看咱們使用最多的 dependencies
dependencies { // This dependency is found on compile classpath of this component and consumers. implementation 'com.google.guava:guava:26.0-jre' implementation('com.google.guava:guava:26.0-jre') // Use JUnit test framework testImplementation 'junit:junit:4.12' }
在 Java 和 Android 項目中 implementation
是必定會用到的,可是一個 Gradle Basic 項目是沒有 implementation
的,實際上,在 dependencies
是不能直接添加任何依賴的。
這裏咱們有說一下 Gradle 怎麼解決依賴。
Gradle 空白項目沒有編譯 Java 項目的能力,可是它能從倉庫下載依賴的庫而且配置到 Project
中。在咱們編譯 Java 項目的時候,一個配置是不夠的,至少要有個測試版,正式版,兩個版本依賴的庫多是不同的,兩個版本部分代碼也是不同的,那麼咱們怎麼區分呢?在 Gradle 中,是經過 configurations
,也就是配置,每一個配置能夠單獨的添加依賴,在編譯的時候,也就是執行某個 Task 的時候,經過讀取配置中的依賴來添加 classpath
,例如:
repositories { mavenCentral() } configurations { test release } dependencies { test 'org.apache.commons:commons-lang3:3.0' release 'org.slf4j:slf4j-log4j12:1.7.2' } task buildTest { doLast { println configurations.test.name println configurations.test.asPath } }
執行 ./gradlew buildTest -q
,輸出結果爲:
test /Users/xxx/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.0/8873bd0bb5cb9ee37f1b04578eb7e26fcdd44cb0/commons-lang3-3.0.jar
若是在 buildTest
這個 Task 中進行編譯工做的話,咱們就能夠直接讀取 configurations.test
的路徑設置爲 classpath
。
implementation
就是經過添加了一個 implementation
配置來實現的。這個配置是經過:
plugins { // Apply the java plugin to add support for Java id 'java' // Apply the application plugin to add support for building an application id 'application' }
添加的,咱們經過 plugins
能夠給 Project
添加屬性,Tasks,配置,例如咱們寫一個最簡單的插件:
package com.demo import org.gradle.api.Plugin import org.gradle.api.Project class DemoPlugin implements Plugin<Project> { void apply(Project project) { project.task("hello") { doLast { println "Hello World" } } project.configurations { demoCompile } } }
這個插件爲 Project
添加了一個 Task,添加了一個配置,咱們將這個文件 DemoPlugin.groovy
放在項目根目錄下的 buildSrc/src/main/groovy/demo/
下,就能夠在 build.gradle
中直接使用了:
apply plugin: com.demo.DemoPlugin
對於 buildscript
,例如:
buildscript { repositories { mavenCentral() google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.3.0' } }
它的做用是爲構建腳本提供依賴,例如咱們在項目中使用了 Android 的 Plugin,這個 Plugin 的要從哪找下載?這就須要在 buildscript 中指定。