什么是gradle
Gradle 是新一代的自動化構(gòu)建工具,它是一個獨(dú)立的項目,跟 AS、Android 無關(guān),官方網(wǎng)站:https://gradle.org/ , 類似 Ant、Maven這類構(gòu)建工具都是基于 xml 來進(jìn)行描述的,很臃腫,而 Gradle 采用的是一種叫做 Groovy 的語言,語法跟 Java 語法很像,但是是一種動態(tài)語言,而且在 Java 基礎(chǔ)上做了不少改進(jìn),用起來更加簡潔、靈活,而且 Gradle 完全兼容 Maven、Ivy,這點(diǎn)基本上宣布了 Maven、Ivy 可以被拋棄了,Gradle 的推出主要以 Java 應(yīng)用為主,當(dāng)然目前還支持 Android、C、C++。
Gradle 與 Android Studio 的關(guān)系
上面也提到,Gradle 跟 Android Studio 其實(shí)沒有關(guān)系,但是 Gradle 官方還是很看重 Android 開發(fā)的,Google 在推出 AS 的時候選中了 Gradle 作為構(gòu)建工具,為了支持 Gradle 能在 AS 上使用,Google 做了個 AS 的插件叫 Android Gradle Plugin ,所以我們能在 AS 上使用 Gradle 完全是因為這個插件的原因。在項目的根目錄有個 build.gradle 文件,里面有這么一句代碼:
classpath 'com.android.tools.build:gradle:2.1.2'
這個就是依賴 gradle 插件的代碼,后面的版本號代表的是 android gradle plugin 的版本,而不是 Gradle 的版本,這個是 Google 定的,跟 Gradle 官方?jīng)]關(guān)系。
Gradle Wrapper
現(xiàn)在默認(rèn)新建一個項目,然后點(diǎn)擊 AS 上的運(yùn)行,默認(rèn)就會直接幫你安裝 Gradle ,我們不需要額外的安裝 Gradle 了,但是其實(shí)這個 Gradle 不是真正的 Gradle ,他叫 Gradle Wrapper ,意為 Gradle 的包裝,什么意思呢?假設(shè)我們本地有多個項目,一個是比較老的項目,還用著 Gradle 1.0 的版本,一個是比較新的項目用了 Gradle 2.0 的版本,但是你兩個項目肯定都想要同時運(yùn)行的,如果你只裝了 Gradle 1.0 的話那肯定不行,所以為了解決這個問題,Google 推出了 Gradle Wrapper 的概念,就是他在你每個項目都配置了一個指定版本的 Gradle ,你可以理解為每個 Android 項目本地都有一個小型的 Gradle ,通過這個每個項目你可以支持用不同的 Gradle 版本來構(gòu)建項目。
Gradle兩個基本概念:項目和任務(wù)
每個 build.gradle 構(gòu)建腳本文件代表一個項目 project:
任務(wù) task 定義在構(gòu)建腳本里:
每次構(gòu)建至少包括一個項目,每個項目里又至少包括一個任務(wù)。
在編譯過程中, Gradle 會根據(jù) build 相關(guān)文件,聚合所有的project和task,執(zhí)行task 中的 action。因為 build.gradle文件中的task非常多,先執(zhí)行哪個后執(zhí)行那個需要一種邏輯來保證。這種邏輯就是依賴邏輯,幾乎所有的Task 都需要依賴其他 task 來執(zhí)行,沒有被依賴的task 會首先被執(zhí)行。所以到最后所有的 Task 會構(gòu)成一個 有向無環(huán)圖(DAG Directed Acyclic Graph)的數(shù)據(jù)結(jié)構(gòu)。
構(gòu)建生命周期
一個 Gradle 構(gòu)建通常包括下面三個階段:
- 初始化
項目實(shí)例會在這時被創(chuàng)建,如果這個項目里有多個 module,或者依賴多個 library,并且它們都有對應(yīng)的 build.gradle 文件,就會創(chuàng)建多個項目實(shí)例 - 配置
在這個階段構(gòu)建腳本被執(zhí)行,并且為每個項目實(shí)例創(chuàng)建和配置任務(wù) - 執(zhí)行
在這個階段 Gradle 將根據(jù)構(gòu)建腳本的配置決定哪些任務(wù)會被執(zhí)行
初識Gradle文件
我們用 Android Studio 新創(chuàng)建一個項目時,會自動生成 3 個 Gradle 文件:
setting.gradle
setting.gradle 文件在 初始化過程中被執(zhí)行,構(gòu)建器通過 setting.gradle 文件中的內(nèi)容了解哪些模塊將被 build,下面的內(nèi)容表明當(dāng)前項目中除了 app 模塊還有另外一個叫做 “shixinlibrary” 的依賴模塊:
include ‘:app’, ‘:shixinlibrary’
注意:單模塊項目不一定需要有 setting 文件,但一旦有多個模塊,必須要有 setting 文件,同時也要寫明所有要構(gòu)建的模塊,否則 gradle 不會 build 不包括的模塊。
主目錄下的 build.gradle
看 gradle 文件中的注釋:
Top-level build file where you can configuration options common to all sub-projects/modules.
主目錄下的 build.gradle 文件是最頂層的構(gòu)建文件,這里配置所有模塊通用的配置信息。
默認(rèn)的頂層 build.gradle 文件中包括兩個代碼塊 (buildscript 和 allprojects):
buildscript
從名字就可以看出來,buildscript 是所有項目的構(gòu)建腳本配置,主要包括依賴的倉庫和依賴的 gradle 版本。
上圖中 repositories 代碼塊將 jcenter 配置為一個倉庫,JCenter 是一個很有名的 Maven 倉庫。確定了依賴的倉庫后,我們就可以在 dependencies 代碼塊中添加依賴的、在 jcenter 倉庫中的包了。
如果倉庫有密碼,也可以同時傳入用戶名和密碼
repositories {
jcenter()
mavenCentral()
maven {
url "https://dl.bintray.com/thelasterstar/maven/"
credentials {
username 'user'
password 'secretpassword'
}
}
}
dependencies 代碼塊用于配置構(gòu)建過程中的依賴包,注意,這里是用于構(gòu)建過程,因此你不能將你的應(yīng)用模塊中需要依賴的庫添加到這里。
默認(rèn)情況下唯一被用于構(gòu)建過程中的依賴包是 Gradle for Android 的插件。我們還可以添加一些其他用于構(gòu)建的插件,比如 retrolambda, apt, freeline 等等。
allprojects
allprojects 代碼塊用來聲明將被用于所有模塊的屬性,注意是所有模塊。常見的就是配置倉庫地址(jcenter, 自定義 maven 倉庫等),你還可以在 allprojects 中創(chuàng)建 tasks,這些 tasks 最終會運(yùn)用到所有模塊中,
官方建議盡量少添加用于所有模塊的屬性,因為這意味著強(qiáng)耦合,一旦沒有構(gòu)建主項目,你的子模塊很有可能因為缺少所有模塊的屬性導(dǎo)致構(gòu)建失敗。
模塊下的 build.gradle
模塊下的 build.gradle 文件只應(yīng)用于當(dāng)前模塊,你可以覆蓋主目錄下的 build.gradle 的內(nèi)容。
上圖中主要分三個模塊:apply plugin , android, dependencies。
apply plugin
apply plugin 聲明了接下來要用到哪些插件的內(nèi)容,上圖表明使用了 androd 插件,這里之所以能用 android 插件,是因為主目錄中聲明了 Gradle for Android 的依賴,這里才能使用。
因此當(dāng)我們需要使用其他插件,比如 retrolambda 時,首先需要在主目錄 build.gradle 文件中添加依賴,然后在模塊 build.gradle 中聲明使用 retrolambda 插件。
備注:默認(rèn)的 android 插件是由 Google 官方維護(hù)的,為我們提供了構(gòu)建、測試、打包 Android 應(yīng)用的能力。除此之外我們還可以自定義插件。在逐漸加深對 Gradle 的了解后,我們將嘗試自己寫個 Gradle 插件。
android
在聲明了 android 插件后,我們就可以使用 android 插件提供的內(nèi)容進(jìn)行構(gòu)建配置。
android 構(gòu)建配置中必須要有的是兩個版本:
- compileSdkVersion : 編譯應(yīng)用的 Android API 版本
- buildToolsVersion : 構(gòu)建工具版本 (android studio3.0上已經(jīng)不需要了)
defaultConfig 代碼塊用于配置應(yīng)用的默認(rèn)屬性,可以覆蓋 AndroidManifest.xml 中的屬性,比如:
- applicationId : 覆蓋了 AndroidManifest 中的 package name
- minSdkVersion : 覆蓋了 AndroidManifest 中的屬性,配置運(yùn)行應(yīng)用的最小 API
- targetSdkVersion : 一樣,用于通知系統(tǒng)當(dāng)前應(yīng)用已經(jīng)被這個版本測試過,和之前的 compileSdkVersion 沒有關(guān)系
- versionCode : 一樣,應(yīng)用的版本號
- versionName : 版本名稱
defaultConfig 還可以添加簽名,占位符等等,這里只列這些。
buildTypes 用來定義如何構(gòu)建和打包不同類型的應(yīng)用,常見的就是測試和生產(chǎn)。
android 中還可以配置其他信息,比如 簽名、渠道等,你可以在 Project Structure 面板中直觀的查看,添加,也可以使用代碼添加
dependencies
上圖中可以看到 依賴配置 在 android 代碼塊的外邊,事實(shí)上依賴配置是 Gradle 配置的基礎(chǔ)功能,也就是說除了 Android,其他類型的項目(比如 JavaEE )也可以這么用。
我們可以在依賴配置中,添加要使用的庫,當(dāng)然也可以添加本地的 jar 包。
Android項目中的Gradle實(shí)踐
版本的統(tǒng)一管理
當(dāng)我們的工程中有許多module的時候,分開管理編譯版本,minsdk將會是一件很麻煩的事,因為一個library的改動,可能會影響到其他module。這時我們就需要對所有的版本進(jìn)行統(tǒng)一的管理:
我們可以把一些需要用的字段都放在project的build.gradle(注意是project的不是module的)中:
ext {
compileSdk = 21
minSdk = 11
targetSdk = 23
support = "23.1.1"
buildTools = "21.0.1"
buildstyle ="debug"
}
這樣,在module的build.gradle中可以進(jìn)行讀取:
defaultConfig {
applicationId "android.com.testgradle"
minSdkVersion rootProject.ext.minSdk
targetSdkVersion rootProject.ext.targetSdk
versionCode 1
versionName "1.0"
}
多版本打包( Build Variant)
BuildType
默認(rèn)情況下,Android plugin會自動的構(gòu)建release和debug兩個版本
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
}
}
// release版本中設(shè)置了開啟混淆,并且定義了混淆文件的位置
Android plugin允許自定義這兩個示例,并且可以創(chuàng)建其他的buildType,如下:
buildTypes {
debug {
minifyEnabled false
applicationIdSuffix ".debug"
}
custom.initWith(buildTypes.debug)
custom {
applicationIdSuffix ".custom"
versionNameSuffix "-customs"
}
}
上述配置進(jìn)行了一下設(shè)置:
- 對默認(rèn)的debug構(gòu)建類型進(jìn)行了修改,關(guān)閉了混淆配置,添加applicationId后綴
- 以debug為基礎(chǔ)創(chuàng)建一個叫custom的構(gòu)建類型(相當(dāng)于繼承了debug版本),在custom的構(gòu)建類型中修改applicationId后綴,并添加了versionName的后綴。
Source sets
gradle通過一個叫SourceSets的概念來尋找和關(guān)聯(lián)要編譯的源碼文件,每當(dāng)創(chuàng)建一個新的build type 的時候,gradle 默認(rèn)都會創(chuàng)建一個新的source set。我們可以建立與main文件夾同級的文件夾,根據(jù)編譯類型的不同我們可以選擇對某些源碼和資源文件直接進(jìn)行替換。
對于每一個BuildType,Android plugin都會創(chuàng)建一個對應(yīng)的sourceSet,默認(rèn)位置為:src/BuildTypeName
所以新建BuildType的名字不能是main、androidTest和test這三個已經(jīng)被用的名字
BuildType的代碼/資源會以以下方式進(jìn)行合并
- manifest會被合并到app的manifest文件中
- res目錄下的資源文件會替換main里的資源文件
- java目錄下的文件會被添加到main里的java目錄中,所以不能和main里的類重名(含包名)
除此之外,不同編譯類型的項目,我們的依賴都可以不同,比如,如果我需要在staging和debug兩個版本中使用不同的log框架,我們這樣配置:
Product flavors
Product flavors 默認(rèn)不引用,我們可以手動添加,同Build type一樣,每一個Product flavors 版本也都有屬于自己的Source sets,也可以差異化設(shè)置構(gòu)建屬性。
當(dāng)同時設(shè)置了BuildType和Product flavors后,二者會組合構(gòu)建,最多可以生成m×n個版本的apk包。對于二者SourceSets,重復(fù)的res文件覆蓋原則buildType>Product flavors
在一些情況下,一個應(yīng)用可能需要基于多個標(biāo)準(zhǔn)來創(chuàng)建多個版本。
例如,有個 app 需要一個免費(fèi)版本和一個付費(fèi)的版本,并且需要在不同的 app 發(fā)布平臺發(fā)布。這個 app 需要 2 個付費(fèi)版和 2 個特定發(fā)布平臺,因此就需要生成 4 個APK(不算 Build Types 生成的 Variant 版本)。
然而,這款 app 中,為 2 個發(fā)布平臺構(gòu)建的付費(fèi)版本源代碼都是相同,因此創(chuàng)建 4 個 flavor 來實(shí)現(xiàn)不是一個好辦法。 如果使用兩個 flavor 維度,兩兩組合,構(gòu)建所有可能的 Variant 組合才是最好的。
這個功能的實(shí)現(xiàn)就是使用 Flavor Dimensions 。每一個 Dimensions 代表一個維度,并且 flavor 都被分配到一個指定的 Dimensions 中。
android {
...
flavorDimensions 'price', 'store'
productFlavors {
google {
dimension 'store'
}
amazon {
dimension 'store'
}
free {
dimension 'price'
}
paid {
dimension 'price'
}
}
}
/* 注:
dimension參數(shù)在gradle2.0之后替換掉了flavorDimension參數(shù),所以很多文章里依然使用的類似于如下寫法:
google {
flavorDimension 'store'
}
flavorDimension參數(shù)現(xiàn)已失效
*/
andorid.flavorDimensions 數(shù)組按照先后排序定義了可能使用的 Dimensions 。每一個 Product Flavor 都被分配到一個 Dimensions 中。
上面的例子中將 Product Flavor 分為兩組(即兩個維度),分別為 price 維度 [free, paid] 和 store 維度 [google, amazon] ,再加上默認(rèn)的 Build Type 有 [debug, release] ,這將會組合生成以下的 Build Variant:
- free-google-debug
- free-google-release
- free-amazon-debug
- free-amazon-release
- paid-google-debug
- paid-google-release
- paid-amazon-debug
- paid-amazon-release
每一個 Variant 版本的配置由幾個 Product Flavor 對象決定:
- 一個來自 price 組中的對象
- 一個來自 store 組中的對象
android.flavorDimensions 中定義的 Dimensions 排序非常重要(Variant 命名和優(yōu)先級等)。
flavorDimensions 中的排序決定了哪一個 flavor 覆蓋哪一個,這對于資源來說非常重要,因為一個 flavor 中的值會替換定義在低優(yōu)先級的 flavor 中的值。
flavorDimensions 使用最高的優(yōu)先級定義,因此在上面例子中的優(yōu)先級為:
price > store > defaultConfig
android studio 3.0的一些變化
不再需要指定則不需要再指定buildToolsVersion了,會根據(jù)compileSdkVersion自動匹配合適的版本。
compile標(biāo)簽依然可用,但是已經(jīng)過時,替代標(biāo)簽為api和implementation
api標(biāo)簽==compile
在java文件中,選中你要轉(zhuǎn)換的代碼,然后在頂部選擇Code——>Convert Java File to Kotlin File進(jìn)行轉(zhuǎn)換就好了,轉(zhuǎn)換之后,這就是一個Kotlin文件了。
Android Monitor分離為logcat和Android Profiler
支持java1.8,支持lamda表達(dá)式,并提示轉(zhuǎn)換
新的文件資源管理器