什么是Gradle
Gradle 是新一代的自動化構建工具,它是一個獨立的項目,跟 AS、Android 無關,類似 Ant、Maven這類構建工具都是基于XML來進行描述的,很臃腫,而 Gradle 采用的是一種叫做 Groovy 的語言,語法跟 Java 語法很像,但是是一種動態語言,而且在 Java 基礎上做了不少改進,用起來更加簡潔、靈活,而且 Gradle 完全兼容 Maven、Ivy,這點基本上宣布了 Maven、Ivy 可以被拋棄了,Gradle 的推出主要以 Java 應用為主,當然目前還支持 Android、C、C++。
Gradle是一種構建工具,它通過編寫一個名為build.gradle的腳本文件對項目進行設置,再根據這個腳本對項目進行構建(復雜的項目也有其他文件),幫你管理項目中的差異、依賴、編譯、打包、部署......你可以定義滿足自己需要的構建邏輯,寫入到build.gradle中供日后復用。
Gradle 腳本本質上就是Groovy腳本,只不過高度利用了Groovy的語法糖,例如省略方法參數括號和省略句尾分號等,讓代碼看起來像DSL(特定領域語言)。Groovy語法跟 Java 語法很像,但是是一種動態語言,而且在 Java 基礎上做了不少改進,用起來更加簡潔、靈活,所以幾乎所有Java和Groovy支持的語法,它的腳本都支持,而且 Gradle 完全兼容 Maven、Ivy。
構建工具
我們以前開發都是用 Eclipse ,而Eclipse 大家都知道是一種 IDE(集成開發環境),最初是用來做 Java 開發的,而 Android 是基于 Java 語言的,所以最初 Google 還是希望 Android 能在 Eclipse 上進行開發,為了滿足這個需求,Google 開發了一個叫 ADT (Android Developer Tools)的東西,相信以前從 Eclipse 時代過來的對 ADT 應該都不陌生,正是因為有了 ADT ,從此我們只需要碼好代碼,然后直接在Eclipse 上進行編譯、運行、簽名、打包等一系列流程,而這背后的工作都是 ADT 的功勞。某種意義上 ADT 就是我們的構建工具。
而自 Google 推出 Android Studio 以來,就宣布默認使用 Gradle 來作為構建工具,并且之后放棄更新 ADT ,從此 Gradle 走入 Android 開發者的視野,而我也是在 AS 的 Beta 版開始接觸并學習 Gradle。
一般來說,構建工具除了以上提到的編譯、運行、簽名、打包等,還具備依賴管理的功能,什么是依賴管理呢?還是拿 Eclipse 來說,我們以前在 Eclipse 上開發 Android ,如果需要用到第三方庫的時候一般都是先下載 jar 文件,然后把 jar 文件添加到 libs 目錄,然后項目中就可以引用了。但是你不覺得這種管理方式很麻煩么?假設第三方庫有更新,需要下載最新的 Jar 文件,然后替換掉原來的,引用的庫少還好,一旦引用的第三方庫多,那簡直麻煩死,可以說這種方式只有依賴,而沒有管理。
現在大家不陌生的 Gradle 引用第三方庫方式是這樣的:compile 'com.android.support:support-v4:24.0.1'
?
類似這樣的依賴方式,是不是很方便?而且很直觀,直接可以看到源地址,升級的話直接改下版本號就可以了,這就是所謂的依賴管理。
所以構建工具就是對你的項目進行編譯、運行、簽名、打包、依賴管理等一系列功能的合集,傳統的構建工具有 Make、Ant、Maven、Ivy等,而 Gradle 是新一代的自動化構建工具。
Ant編寫容易,但功能有限,需要人工操作的過程也多;Maven依托于龐大的依賴倉庫,因此有著強大的外部依賴管理,但添加本地依賴并不方便,且項目不能靈活修改。而Gradle能很好地結合Ant與Maven各自的優點,可以隨意的編寫任務并組合成項目,直接利用Maven倉庫,并且能很好的支持傳遞依賴和內部依賴。
通俗一點類比成吃飯的話,大致就是:Ant是自己買菜洗菜燒水做飯,Maven是去飯店點餐,Gradle是3D打印食物。
Gradle應用場景
差異管理
國內有n個Android市場,n個手機品牌,n個手機尺寸......,一般公司都會針對不同的市場單獨發包用來統計不同渠道的下載量等情況,可能需要針對不同(品牌,尺寸等各種硬件信息)的手機做一些特殊的處理,這個時候你可以針對不同的情況單獨建一個工程,或者更好一點你可以通過一些變量來控制,像這樣:
if(isMoto){
do something
}else if(isHuawei){
do something
}
依賴管理
軟件開發可能需要依賴各種不同的jar、library,可以通過將.jar/library工程下載到本地再copy到你的工程中,但不知你是否聽說過國外有個叫中央倉庫的東西,在這個倉庫里你可以找到所有你能想到以及你從來沒聽說過的jar、aar......這里可以找到所有你需要的依賴,而你需要的只是指定一個坐標
compile 'com.android.support:support-v4:24.0.1'
剩下的依賴的尋找、下載、添加到classpath等你都不需要去關心
什么是Gradle插件?
在使用Android Studio進行開發的時候,我們創建一個Android工程,會默認生成一個build.gradle腳本,打開腳本你會看到以下代碼:
apply plugin: 'com.android.application'
如果我們創建一個Library的話,就會變成:
apply plugin: 'com.android.library'
其實這就是在Gradle腳本引用Android為我們提供的插件,plugin表示插件,’com.android.application’表示我們引用的插件名,通常是以包名來命名。
這個插件是怎么被引用進來的?我們可以在根目錄找到另外一個build.gradle文件,打開可以看到以下代碼:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
- buildscript方法是定義了全局的相關屬性
- repositories定義了jcenter作為倉庫。一個倉庫代表著你的依賴包的來源,例如maven倉庫
- dependencies用來定義構建過程
- classpath ‘com.android.tools.build:gradle:2.0.0’,就是將遠程的插件下載到本地并將其構建到我們工程當中
Androd應用插件其實就是Gradle插件,用于提供Gradle構建環境,支持其在AndroidStudio中使用,通過classpath 'com.android.tools.build:gradle:1.2.3'
獲取插件,apply plugin: 'java
應用插件。
理解Gradle腳本
當然我們現在討論的所有內容都是基于Android Studio的,所以請先行下載相關工具。當我們創建一個新的工程,Android Studio會默認為我們創建三個gradle文件,兩個build.gradle,一個settings.gradle,build.gradle分別放在了根目錄和moudle目錄下,下面是gradle文件的構成圖:
MyApp
├── build.gradle
├── settings.gradle
└── app
└── build.gradle
setting.gradle解析
當你的app只有一個模塊的時候,你的setting.gradle將會是這樣子的:
include ':app'
setting.gradle文件將會在初始化時期執行,關于初始化時期,并且定義了哪一個模塊將會被構建。舉個例子,上述setting.gradle包含了app模塊,setting.gradle是針對多模塊操作的,所以單獨的模塊工程完全可以刪除掉該文件。在這之后,Gradle會為我們創建一個Setting對象,并為其包含必要的方法,你不必知道Settings類的詳細細節,但是你最好能夠知道這個概念。
build.gradle的配置文件
基于grade構建的項目通常至少有一個build.gradle,那么我們來看看Android的build.gradle:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
這個就是實際構建開始的地方,在倉庫地址中,我們使用了JCenter,JCenter類似maven庫,不需要任何額外的配置,grade還支持其他幾個倉庫,不論是遠程還是本地倉庫。
構建腳本也定義了一個Android構建工具,這個就是Android plugin(Android應用插件)的來源之處。Android plugin提供了所有需要去構建和測試的應用。每個Android應用都需要這么一個插件:
apply plugin: 'com.android.application'
Android應用插件用于擴展Gradle腳本的能力,在一個項目中使用插件,這樣該項目的構建腳本就可以定義該插件定義好的屬性和使用它的tasks。
這里的Androd應用插件其實就是Gradle插件,用于提供Gradle構建環境,支持其在AndroidStudio中使用,通過classpath 'com.android.tools.build:gradle:1.2.3'
獲取插件,apply plugin: 'java
應用插件
注意:當你在開發一個依賴庫,那么你應該使用'com.android.library',并且你不能同時使用他們2個,這將導致構建失敗,一個模塊要么使用Android application或者Android library插件,而不是二者。
當使用Android 插件的時候,Android標簽將可以被使用,如下所示:
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
}
根目錄的build.gradle
該gradle文件是定義在這個工程下的所有模塊的公共屬性,它默認包含二個方法:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
allprojects {
repositories {
jcenter()
}
}
repositories {
mavenCentral()
}
buildscript方法是定義了全局的相關屬性,repositories定義了jcenter作為倉庫。一個倉庫代表著你的依賴包的來源,例如maven倉庫。dependencies用來定義構建過程。這意味著你不應該在該方法體內定義子模塊的依賴包,你僅僅需要定義默認的Android插件就可以了,因為該插件可以讓你執行相關Android的tasks。
allprojects方法可以用來定義各個模塊的默認屬性,你可以不僅僅局限于默認的配置,未來你可以自己創造tasks在allprojects方法體內,這些tasks將會在所有模塊中可見。
為什么repositories要聲明兩次哪?buildscript代碼塊中的聲明與下半部分聲明有什么不同?buildscript中的聲明是gradle腳本自身需要使用的資源。可以聲明的資源包括依賴項、第三方插件、maven倉庫地址等。而在build.gradle文件中直接聲明的依賴項、倉庫地址等信息是項目自身需要的資源。
模塊內的build.gradle
模塊內的gradle文件只對該模塊起作用,而且其可以重寫任何的參數來自于根目錄下的gradle文件。模塊內的build.gradle主要分三個模塊:apply plugin , android, dependencies。
該模塊文件應該是這樣:
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.gradleforandroid.gettingstarted"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile
('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
}
apply plugin
apply plugin 聲明了接下來要用到哪些插件的內容,上圖表明使用了 Androd 插件,這里之所以能用 Android 插件,是因為主目錄中聲明了 Gradle for Android 的依賴,這里才能使用。Android應用插件,該插件其是Google的Android開發團隊編寫的插件,能夠提供所有關于Android應用和依賴庫的構建,打包和測試。
因此當我們需要使用其他插件,比如 retrolambda 時,首先需要在主目錄 build.gradle 文件中添加依賴,然后在模塊 build.gradle 中聲明使用 retrolambda 插件。
備注:默認的 Android 插件是由 Google 官方維護的,為我們提供了構建、測試、打包 Android 應用的能力。除此之外我們還可以自定義插件。在逐漸加深對 Gradle 的了解后,我們將嘗試自己寫個 Gradle 插件。
android
在聲明了 Android 插件后,我們就可以使用 Android 插件提供的內容進行構建配置。
android 構建配置中必須要有的是兩個版本:
- compileSdkVersion : 編譯應用的 Android API 版本
- buildToolsVersion : 構建工具版本
- 構建工具包括 aapt, zipalign, renderscript 等
- 用于在打包時生成各種中間產物,可以從 SDK Manager 中下載構建工具
defaultConfig 代碼塊用于配置應用的默認屬性,可以覆蓋 AndroidManifest.xml 中的屬性,比如:
- applicationId : 覆蓋了 AndroidManifest 中的 package name
- minSdkVersion : 覆蓋了 AndroidManifest 中的屬性,配置運行應用的最小 API
- targetSdkVersion : 一樣,用于通知系統當前應用已經被這個版本測試過,和之前的 compileSdkVersion 沒有關系
- versionCode : 一樣,應用的版本號
- versionName : 版本名稱
defaultConfig 還可以添加簽名,占位符等等,這里只列這些。
buildTypes 用來定義如何構建和打包不同類型的應用,常見的就是測試和生產。
android 中還可以配置其他信息,比如 簽名、渠道等,你可以在 Project Structure 面板中直觀的查看,添加,也可以使用代碼添加。
dependencies
上圖中可以看到 依賴配置 在 android 代碼塊的外邊,事實上依賴配置是 Gradle 配置的基礎功能,也就是說除了 Android,其他類型的項目(比如 JavaEE )也可以這么用。
依賴模塊作為Gradle默認的屬性之一,為你的App定義了所有的依賴包。
在配置完項目倉庫后,我們可以聲明其中的依賴,如果我們想要聲明一個新的依賴,可以采用如下步驟:
- 指定依賴的配置。
- 聲明所需的依賴。
有些時候,你可能需要和SDK協調工作。為了能順利編譯你的代碼,你需要添加SDK到你的編譯環境。你不需要將SDK包含在你的APK中,因為它早已經存在于設備中,所以配置來啦,我們會有5個不同的配置:
- compile
- apk
- provided
- testCompile
- androidTestCompile
compile是默認的那個,其含義是包含所有的依賴包,即在APK里,compile的依賴會存在。
apk的意思是apk中存在,但是不會加入編譯中,這個貌似用的比較少。
provided的意思是提供編譯支持,但是不會寫入apk。
testCompile和androidTestCompile會添加額外的library支持針對測試。
這些配置將會被用在測試相關的tasks中,這會對添加測試框架例如JUnit或者Espresso非常有用,因為你只是想讓這些框架們能夠出現在測試apk中,而不是生產apk中。
除了這些特定的配置外,Android插件還為每個構建變體提供了配置,這讓debugCompile或者releaseProvided等配置成為可能。如果你想針對你的debug版本添加一個logging框架,這將很有用。
一個依賴需要定義三個元素:group,name和version。group意味著創建該library的組織名,通常這會是包名,name是該library的唯一標示。version是該library的版本號,我們來看看如何申明依賴:
dependencies {
compile 'com.google.code.gson:gson:2.3'
compile 'com.squareup.retrofit:retrofit:1.9.0'
}
上述的代碼是基于Groovy語法的,所以其完整的表述應該是這樣的:
dependencies {
compile group: 'com.google.code.gson', name: 'gson', version:'2.3'
compile group: 'com.squareup.retrofit', name: 'retrofit', version: '1.9.0'
}
Gradle腳本的執行時序
Gradle腳本的執行分為三個過程:
-
初始化
分析有哪些Module將要被構建,為每個Module創建對應的 Project實例。這個時候settings.gradle
文件會被解析,setting.gradle是針對多模塊操作的,定義了哪一個模塊將會別構建。 - 配置:處理所有的模塊的 build.gradle 腳本,處理依賴、屬性等。這個時候每個模塊的build.gradle文件會被解析并配置,這個時候會構建整個Task的鏈表(這里的鏈表僅僅指存在依賴關系的Task的集合,不是數據結構的鏈表)。
- 執行:根據Task鏈表來執行某一個特定的Task,這個Task所依賴的其他Task都將會被提前執行。
Project和tasks
在Grade中的兩大重要的概念,分別是Project和Tasks。每一次構建都是有至少一個Project來完成,所以Android Studio中的Project和Gradle中的Project不是一個概念。每個Project有至少一個Tasks。每一個build.grade文件代表著一個Project。Tasks在build.gradle中定義。當初始化構建進程,Gradle會基于build文件,集合所有的Project和Tasks,一個Tasks包含了一系列動作,然后它們將會按照順序執行,一個動作就是一段被執行的代碼,很像Java中的方法。