系列目錄
1.【Gradle深入淺出】——初識Gradle
2.【Gradle深入淺出】——Gradle基礎(chǔ)概念
3.【Gradle深入淺出】——Android Gradle Plugin 基礎(chǔ)概念
4.【Gradle深入淺出】——Gradle配置(一)
5.【Gradle深入淺出】——Gralde配置(二)
一、為什么要寫Gradle
Gradle其實(shí)是自己一直想要了解的東西,但是一直沒有下定決心,因?yàn)檫@個東西,你要說很有用,平時常用的可能就是那幾個配置,要說沒用,每次到關(guān)鍵的時候,gradle總會是你對于Android整體學(xué)習(xí)的一個攔路虎。為什么這樣說呢,縱觀現(xiàn)在Android整體的學(xué)習(xí)路線,其實(shí)歸納總結(jié)可以分為三個部分。
- 1.Android基礎(chǔ)路線
也就是我們常說的業(yè)務(wù)開發(fā),對于組件,MVP,MVVM的使用 - 2.Android運(yùn)行時路線
也就是我們平時說的反射,hook等黑科技,而這類黑科技往往是在運(yùn)行時對于系統(tǒng)層面或者源碼層面的動態(tài)調(diào)整,達(dá)到我們的目的。 - 3.編譯期路線
其實(shí)運(yùn)行時做的調(diào)整已經(jīng)能夠完成我們所有的需求,但運(yùn)行時對于性能的影響還有穩(wěn)定性一直被人詬病,所以現(xiàn)在逐漸發(fā)展成編譯期路線,也就是在編譯期做處理,將我們想做的入侵編譯期,最終打到我們的apk包中,達(dá)到我們想要的效果,而由于是在編譯期做的處理,所以是前置處理好的,也就沒有了運(yùn)行時效率的問題。常見的比如組件化,插樁熱修復(fù),Router,AOP,包體積優(yōu)化。
我們其實(shí)可以發(fā)現(xiàn),一個技術(shù)無非就兩種,運(yùn)行時/編譯期,從上面的介紹就會發(fā)現(xiàn),同一個技術(shù),編譯期和運(yùn)行時就有不同的解決方案,比如ARouter早期是用運(yùn)行時反射查找,后面變成編譯期生成映射關(guān)系的類,再比如熱修復(fù)有hook的函數(shù)替換,也有插樁的方式。而且由于現(xiàn)在Android生態(tài)越來越重視性能問題,現(xiàn)在越來越多的庫,開始遷移到編譯期,所以可以看出現(xiàn)在對于編譯的學(xué)習(xí)對于Android的進(jìn)階開發(fā)是非常重要的。
那么既然提到Android的編譯,就肯定脫離不了Gradle的學(xué)習(xí),這里就不展開Android從ADT到Gradle的變化歷程,但是Gradle的強(qiáng)大,讓不僅僅是Android,后端的Java項目也開始使用Gradle來進(jìn)行打包,在了解了Gradle后,才能讓我們進(jìn)一步學(xué)習(xí)Android的各項技術(shù),可以說這個已經(jīng)成為一個攔路虎,或者說基石。
以上是我自己的想法,也是我自己對于Android學(xué)習(xí)的一個理解,所以我想開展一個長篇的Gradle學(xué)習(xí)系列,爭取能較為全面深入的學(xué)習(xí)Gradle。
二、Gradle是什么
以上的內(nèi)容可能來源于其他的一些博客的片段中,以為偏概念的東西,沒辦法做到純原創(chuàng)編寫,所以我做的是精選我認(rèn)為值得學(xué)習(xí)的內(nèi)容。
首先我們看下Gradle官網(wǎng)對于他自己的介紹
From mobile apps to microservices, from small startups to big enterprises, Gradle helps teams build, autom ate and deliver better software, faster.
可以看到Gradle不僅是手機(jī)應(yīng)用,還可以應(yīng)用于后臺,Gradle到使命簡單來說就是幫忙工程構(gòu)建,更快,更自動化,更可控,更便捷。
回到Android側(cè),Gradle是目前Android主流的構(gòu)建工具,不管你是通過命令行還是通過AndroidStudio來build,最終都是通過Gradle來實(shí)現(xiàn)的。
說到打包,我們來看下Java打包到三大山。
Java生態(tài)體系中有三大構(gòu)建工具:Ant、Maven和Gradle。其中,Ant是由Apache軟件基金會維護(hù);Maven這個單詞來自于意第緒語(猶太語),意為知識的積累,最初在Jakata Turbine項目中用來簡化構(gòu)建過程;Gradle是一個基于Apache Ant和Apache Maven概念的項目自動化構(gòu)建開源工具,它使用一種基于Groovy的特定領(lǐng)域語言(DSL)來聲明項目設(shè)置,拋棄了基于XML的各種繁瑣配置。
三、Gradle/Groovy/Java/JVM/Kotlin關(guān)系
最初看到Gradle的時候會發(fā)現(xiàn)他和Java的語法很像 ,但又有區(qū)別,再到后面了解Groovy,學(xué)習(xí)Kotlin,他們之間的關(guān)系對于新學(xué)習(xí)Gradle系統(tǒng)的人來說會很懵。
如果來畫一個他們之間的關(guān)系,其實(shí)就比較清楚的體現(xiàn)了他們之間的關(guān)系。

從上面我們會有一個比較直觀的理解,首先從上到下,Gradle不屬于Language層,就像上面描述的,Gradle是一個框架,是一個幫助我們工程編譯的框架。而往下的Language層,就是我們的Java/Kotlin/Groovy,雖然三個都屬于Language層,但是還是有一定的歸屬關(guān)系,Groovy和Kotlin是基于Java的DSL,所以Groovy和Kotlin是可以無縫調(diào)用Java的。
DSL:Domain Specific Language 的縮寫,中文的翻譯為領(lǐng)域特定語言(下簡稱DSL);而與DSL相對的就是GPL,這里的GPL并不是我們知道的開源許可證,而是General Purpose Language的簡稱,即通用編程語言,也就是我們非常熟悉的Objective-C、Java、Python、以及C語言等。
DSL 通過在表達(dá)能力上做的妥協(xié)換取在某一領(lǐng)域內(nèi)的高效。它們的表達(dá)能力有限,只是在特定領(lǐng)域解決特定任務(wù)的。
我對DSL的理解是,DSL是依附于某一特殊場景,或者專注于某一領(lǐng)域,通過大量的語法糖,來便捷這一領(lǐng)域的開發(fā)。有來這樣的理解,我們來看下屬于DSL有哪些:SQL,CSS,HTML,Groovy,Kotlin。會發(fā)現(xiàn)特征就很明顯。所以可以看到Groovy和Kotlin都是基于Java的DSL,所以再回頭往上看,Gradle可以用Kotlin/Java編寫嗎?答案肯定是,完全沒問題,只是缺少來語法糖的支持,肯定寫起來會枯草很多。(當(dāng)然還是指的Java,Kotlin編寫Gradle腳本還是很爽的)再往下看,我們就到來真正運(yùn)行的地方,class文件,剛才說到的Groovy/Kotlin/Java,最終都會編譯成class文件,變成JVM能夠運(yùn)行的字節(jié)碼文件,最終運(yùn)行起來。
四、Gradle版本號和Gradle Plugin版本號的關(guān)系
記得剛從Eclipse轉(zhuǎn)到Android Studio的時候,特別痛苦,經(jīng)常各種莫名其妙的報錯,說什么版本對應(yīng)不上。
Project is using an old version of the Android Gradle plug-in. The minimum supported version is x.x.x Please update the version of the dependency 'com.android.tools.build:gradle'
這時候就會很懵,版本對應(yīng)不上,而且都是gradle,怎么一個工程里面有兩個gradle版本,搜索項目會發(fā)現(xiàn)還真是有兩個定義gralde版本的地方。
//build.gradle
classpath 'com.android.tools.build:gradle:3.5.1'
//gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
所以這里剛一看到就會感到很奇怪,這兩個Gradle到底哪個是真正的Gradle,各有什么作用呢?
所以這里就來介紹一下,先說一下Gradle和Android Studio是完全兩個沒有關(guān)系的東西,Gradle有一套自己的環(huán)境,就像我們安裝Java一樣,需要配置環(huán)境變量等等,然后也可以通過命令行執(zhí)行操作,類似于javac,那么怎么能讓AS便捷的使用Gradle呢,所以Google就針對Gradle和AS的結(jié)合,編寫了一個Android Gradle Plugin,所以我們能在AS上使用Gradle完全是因?yàn)檫@個插件的原因。
它本質(zhì)就是一個AS插件,它一邊調(diào)用Gradle本身的代碼和批處理工具來構(gòu)建項目,一邊調(diào)用Android SDK的編譯、打包功能,從而讓我們能夠順暢的在AS上進(jìn)行開發(fā)。
Gradle插件跟Android SDK Build Tool有關(guān)聯(lián),因?yàn)樗€承接著Android Studio里的編譯相關(guān)的功能,這也是我們要在項目的local.properties文件里寫Android SDK路徑,在build.gradle里注明buildToolsVersion的原因。
所以現(xiàn)在我們再來看上兩個定義的版本就會發(fā)現(xiàn),classpath 'com.android.tools.build:gradle:3.0.0'是用來定義Android Gradle Plugin的版本,對應(yīng)的distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip就是用來定義Gradle的版本的。
Gradle插件是獨(dú)立于AndroidStudio運(yùn)行的,所以它的更新也是AndroidStudio分開的。Gradle插件會有版本號,每個版本號又對應(yīng)一個或者一些Gradle發(fā)行版本(一般是限定一個最低版本)
所以我們的插件的版本和Gradle的版本一定要對應(yīng)上,具體的對應(yīng)關(guān)系官方地址
但這里其實(shí)我很想吐槽一下,既然是插件,對于Gradle的包裝,為什么不直接內(nèi)部指定好Gradle的版本號,還需要開發(fā)者自己手動去對應(yīng)Gradle Plugin和Gradle的關(guān)系,從一個插件或者產(chǎn)品的角度來看,我感覺這樣的設(shè)計是有待優(yōu)化的。
五、Gradle文件目錄結(jié)構(gòu)
接下來我們來看下和Gradle相關(guān)的目錄結(jié)構(gòu),通過對目錄結(jié)構(gòu)對了解,也有助于我們后面對于Gradle編譯原理對理解。

首先我們看到了上面的目錄結(jié)構(gòu),這個是我們新建的一個工程的目錄結(jié)構(gòu)。
local.properties
sdk.dir=/xxxxx/xxxxxx/Library/Android/sdk
local.properties是構(gòu)建系統(tǒng)配置本地環(huán)境屬性,其中包括:
- ndk.dir —— NDK路徑。此屬性已被棄用,NDK的所有下載版本都安裝在Android SDK目錄下的NDK目錄中。
- sdk.dir —— SDK的路徑。
- cmake.dir —— CMake的路徑。
- ndk.symlinkdir —— 在Android Studio 3.5以及更高的版本中,創(chuàng)建指向NDK的符號鏈接,該符號鏈接的路徑可比SDK安裝路徑短。
setting.gradle
setting.gradle文件位于項目的根目錄下,用于指示Gradle在構(gòu)建應(yīng)用時將哪些模塊包含在內(nèi)。對于單工程來說,這個文件的作用很小,一般是這樣的。
include ':app'
但是如果我們創(chuàng)建一個子Module,就會發(fā)現(xiàn)這個文件的內(nèi)容發(fā)生了改變。
include ':app'
include ':subModule'
所以這個文件就是當(dāng)我們工程進(jìn)行組件化和模塊化的很重要的文件。
gradle.properties
用于配置項目全局Gradle設(shè)置,如Gradle守護(hù)程序的最大堆大小,有時候我們?nèi)值囊恍┡渲?,比如全局變量,也可以在這個文件進(jìn)行配置,所有的Module都可以讀取到。
但這里要提一個問題:這個文件可以有多個嗎?如果多個的話,會是怎樣的效果呢、這里大家可以自己試下,我這里就直接給結(jié)論了。
這個文件可以多個,但是根目錄下的配置會全局生效,也就是如果我們在根目錄配置了一個變量COMMON__VERSION,所有的子Module都可以讀取到,如果我們在子項目中也新建一個gradle.properties也配置了一個COMMON_VERSION變量,那么會覆蓋根目錄的版本。
gradle--wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-rc-1-all.zip
這個文件的從名字上來看,意思就很直觀,就是包了一層Gradle,其實(shí)他的作用也是這樣的,經(jīng)常有這樣的場景,我們每個人本地的Gradle版本可能不一致,每個項目的Gradle版本也可能不一致,這樣就可能出現(xiàn)版本不一致而導(dǎo)致編譯不通過的問題,所以為了解決這個問題,Gradle官方出了gradle-wrapper的機(jī)制,表示該項目使用什么版本來進(jìn)行編譯,這樣假如我們本地是Gradle2.0,拉下來的gradle--wrapper的配置是gradle-4.1,這時候我們就會使用這個Gradle版本進(jìn)行編譯。
build.gradle
這個就是重頭戲了,這就是我們用于打包編寫的Gradle腳本,里面可以看到我們寫的依賴關(guān)系,打包配置等等一系列配置和打包邏輯,這個這里就不展開講了,后面會專門展開講解,這里還是從文件結(jié)構(gòu)的角度來看下這個文件,這里有兩個build.gradle文件,如果我們看下,同樣的兩個文件層級是不一樣的。
在根目錄下的build.gradle常用于配置我們的全局屬性,當(dāng)在這個文件配置后,我們所有的子項目都會生效、
在子項目目錄下的build.gradle,就是我們針對這個項目的單獨(dú)的配置。
六、總結(jié)
這篇博客從基礎(chǔ)上講解了我認(rèn)為在第一次接觸Gradle可能遇到的一些困惑點(diǎn),算是對Gradle先有了一個大體上的了解,接下來會繼續(xù)講解Gradle,第二篇博客可能對Gradle的配置進(jìn)行講解。