目前國內對Android領域的探索已經越來越深,不少技術領域如插件化、熱修復、構建系統等都對Gradle有迫切的需求,不懂Gradle將無法完成上述事情。所以Gradle必須要學習。
對本文有任何問題,可加我的個人微信:kymjs123
Gradle 裡的幾乎任何東西都是基於這兩個基礎概念:
掌握了這兩個,你就掌握了一大半的 Gradle 知識了。
首先講 Task字面理解為任務,Gradle 中所有執行的事件都是藉由 Task 執行的。
例如我們新建一個 Android 工程,在其根目錄中輸入:
gradle tasks -q
可以看到如下輸出(你可能需要事先配置gradle的環境變量,或也可使用./gradlew替代):
根據上圖可以看到當前工程中的每條task都已羅列出,並且有黃色的輸出表示當前task的描述。
其中-q表示忽略gradle本身的log信息,加上這個參數可以屏蔽很多無關的輸出,不加也不會影響執行。
聲明一個 task 只需要在任務名前面加上task就可以了,例如下面聲明了一個hello的Task。
task hello
通常我們會給task附帶一些執行動作,稱之為Action,例如
hello.doFirst{ println "hello first"}hello.doLast{ println "hello last"}
也可以附帶一個閉包配置,稱之為Configuration,閉包中不僅可用做賦值操作,也可以執行一些自動執行的配置。
hello { println "hello"}
Task依賴單獨聲明一個task在實際開發中幾乎不會有任何的意義,更多的時候是讓多個task組合起來,一個依賴另一個,形成一連串的任務集。
task hellohello.doFirst{ println "hello "}task world(dependsOn: "hello") << { println "world"}
上面這段代碼定義了兩個task,當我們執行hello任務的時候,會輸出 hello,而執行world任務的時候,由於聲明了dependsOn: "hello",表示world依賴hello,會先執行hello,再執行world。
task xxx << {}
這樣的語法等價於
task xxxxxx.dolast {}
你可以在任意位置新建一個名為build.gradle的文本,來練習上面講述的task定義與依賴。
接著講 ProjectAndroid │ ├──app │ └──build.gradle │ ├──library │ └──build.gradle │ ├──*.properties │ ├──build.gradle │ └──setting.gradle
一個 Android 工程,通常是由上述結構構成,其中有著許多不為人知的巧妙用法。
setting.gradle文件關於setting.gradle中也可以寫代碼,是很多人不知道的。如下代碼是我在上一篇文章【企業級 Android 模塊化平臺設計建議】中講到的一個例子,在setting.gradle文件中,可以指定一個project位置,這裡就可以將一個外部工程中的模塊導入到APP工程中了。
getLocalProperties().entrySet().each { entry -> def moduleName = entry.key if (Boolean.valueOf(entry.value)) { def file = new File(rootProject.projectDir.parent, "/${moduleName.replace("\\W", "")}/${moduleName.toLowerCase()}") if (file.exists()) { include ":${moduleName.toLowerCase()}" project(":${moduleName.toLowerCase()}").projectDir = file } }}
build.gradle一個項目的根gradle文件,用於描述這個項目的統一資源,其中包括各子資源的使用方式、插件的依賴環境等等。
subprojects{ apply plugin: 'com.android.library' dependencies { compile 'com.xxx.xxx:xxx:1.0.0' }}
通常我們在每個模塊都會引用的 aar 的時候,都會在每個模塊裡面都去手動的compile一遍,例如support包。 但實際上有一個非常簡單的辦法,寫一遍就可以了,就是在項目的根gradle文件中的subprojects閉包中聲明這個dependencies。
通常在寫compile依賴的時候,我們都會寫成這樣:
compile 'com.android.support:appcompat-v7:25.0.0'
其實在gradle中,這是一個方法調用,它的本質是compile()方法傳入了一個map參數,因此完整的寫法實際上是這樣的:
compile group: 'com.android.support' name:'appcompat-v7' version:'25.0.0'
同時,map 的可使用 key 不只是有常用的group、name、version,還包括不常用的configuration、classifier等等。
再看TaskGroovy 是基於 Java 的,只不過在這基礎上加了一大堆的閉包,來幫助更方便的開發構建腳本。如果你不會 Groovy,沒關係,當成 Java 寫就行了,其實當成 Kotlin 寫是最恰當的。如果你還不會 Kotlin,我強烈推薦你查看我的 【 Kotlin Primer 】系列文章
每個Task都可以配置其輸入與輸出,如果一個Task的輸出與上一次的輸出一致,則不會重複執行。此刻,會在命令行中輸出UP-TO-DATE表示已經是最新的結果。
例如如下Task:
task transform { ext.srcFile = file('hello.txt') ext.destDir = new File(buildDir, 'generated') inputs.file srcFile outputs.dir destDir doLast { destDir.mkdirs() def ins = new BufferedReader(new FileReader(srcFile)) def stringBuilder = new StringBuilder() def temp while ((temp = ins.readLine()) != null) { stringBuilder.append(temp) } def destFile = new File(destDir, "world.txt") destFile.text = stringBuilder.toString() }}
重複執行後會輸出UP-TO-DATE
騷操作的背後學習任何一門技術,最快的途徑就是看源碼,gradle的源碼位於src目錄中,例如在我本機的路徑為:
/Users/zhangtao/.gradle/wrapper/dists/gradle-3.3-all/55gk2rcmfc6p2dg9u9ohc3hw9/gradle-3.3/src
本地新建一個普通Java工程,導入源碼查看代碼與注釋,這是最好的學習資料。
task hello
在 Groovy 中,方法括號可以省略,如果字符串的類型是可以被推斷的,那麼引號也可以省略
public interface org.gradle.api.Project{ Task task(String name); Task task(String name, Closure configureClosure);}public TaskInternal createTask(Map<String, ?> args) {}
閉包的存在,目的是為了更好的為對象初始化。同 Kotlin 一樣,當閉包做為最後一個參數的時候,可以省略括號。
Copy a = task(myCopy, type: Copy)a.from 'resources'a.into 'target'a.include('**/*.txt', '**/*.xml', '**/*.properties')
等價於
task myCopy(type: Copy)myCopy { from 'resources' into 'target' include('**/*.txt', '**/*.xml', '**/*.properties')}
本章就講到這裡,下一篇講如何創建一個Gradle插件,完成編譯時向指定類或新生成類中動態添加代碼(包括jar包中)。