簡(jiǎn)介
Gradle 是用了一種基于 Groovy 的領(lǐng)域特定語(yǔ)言(DSL,Domain Specific Language)來(lái)聲明項(xiàng)目設(shè)置,摒棄了 XML(如 ANT 和 Maven)的各種繁瑣配置。
對(duì)于 Groovy 和 Gradle 的深入理解,這里推薦一篇非常好的博客:深入理解 Android 之 Gradle
Gradle 選擇了 Groovy 這門動(dòng)態(tài)語(yǔ)言。Groovy 基于 Java 并拓展了 Java雅采。 Java 程序員可以無(wú)縫切換到使用 Groovy 開發(fā)程序。Groovy 說(shuō)白了就是把寫 Java 程序變得像寫腳本一樣簡(jiǎn)單慨亲。寫完就可以執(zhí)行婚瓜, Groovy 內(nèi)部會(huì)將其編譯成 Javaclass 然后啟動(dòng)虛擬機(jī)來(lái)執(zhí)行。
總的來(lái)說(shuō)刑棵,Gradle 基于 Groovy 實(shí)現(xiàn)了一套編程框架巴刻,所以我們可以在 Gradle 配置中通過(guò)編程的方式靈活的去配置我們的構(gòu)建過(guò)程。Gradle 本質(zhì)上是執(zhí)行一系列項(xiàng)目構(gòu)建過(guò)程铐望,也就是所謂的自動(dòng)化冈涧。為了更好地利用 Gradle茂附,除了要知道 Gradle 如何使用之外正蛙,我們更要熟悉正在開發(fā)的項(xiàng)目的構(gòu)建過(guò)程。
Android 項(xiàng)目的 .gradle 文件
在 Android Studio 新建一個(gè)項(xiàng)目营曼,生成如下目錄 ↓
├── app #Android App目錄
│ ├── app.iml
│ ├── build #構(gòu)建輸出目錄
│ ├── build.gradle #構(gòu)建腳本
│ ├── libs #so相關(guān)庫(kù)
│ ├── proguard-rules.pro #proguard混淆配置
│ └── src #源代碼乒验,資源等
├── build
│ └── intermediates
├── build.gradle #工程構(gòu)建文件
├── gradle
│ └── wrapper
├── gradle.properties #gradle的配置
├── gradlew #gradle wrapper linux shell腳本
├── gradlew.bat
├── LibSqlite.iml
├── local.properties #配置Androod SDK位置文件
└── settings.gradle #工程配置
觀察目錄,會(huì)生成以下三個(gè) .gradle 文件 ↓
接下來(lái)逐一講解蒂阱。
1. settings.gradle
用于配置 project锻全,標(biāo)明該項(xiàng)目有哪些 module 需要構(gòu)建,如下录煤,則代表包含一個(gè) :app module
include ':app'
2. 工程目錄下的 build.gradle
和 settings.gradle 同處于根目錄下的 build.gradle 是一個(gè)頂級(jí)的 build 配置文件鳄厌,在這里可以為所有 project 以及 module 配置一些常用的配置。
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
// 代碼托管倉(cāng)庫(kù)
jcenter()
}
dependencies {
// Gradle 插件及使用版本
classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
// 代碼托管倉(cāng)庫(kù)
jcenter()
}
}
// 運(yùn)行g(shù)radle clean時(shí)妈踊,執(zhí)行此處定義的task了嚎。
// 該任務(wù)繼承自Delete,刪除根目錄中的build目錄。
// 相當(dāng)于執(zhí)行Delete.delete(rootProject.buildDir)
task clean(type: Delete) {
delete rootProject.buildDir
}
可以看到代碼中有兩處 repositories歪泳,且閉包中都生明了 jcenter() 這個(gè)配置萝勤,其實(shí)它是一個(gè)代碼托管倉(cāng)庫(kù),很多開源的 Android 項(xiàng)目都會(huì)選擇將代碼托管到到 jcenter() 上呐伞,聲明了這個(gè)配置敌卓,我們就可以輕松地引用 jcenter() 上任何的開源項(xiàng)目了。接下來(lái)伶氢,dependencies 閉包中使用 classpath 聲明了一個(gè) Gradle 插件趟径,這是因?yàn)?Gradle 并不是專門為構(gòu)建 Android 項(xiàng)目而開發(fā)的,Java鞍历、C++ 等很多項(xiàng)目都可以使用 Gradle 來(lái)構(gòu)建舵抹,因此我們?nèi)绻胍褂盟鼇?lái)構(gòu)建 Android 項(xiàng)目必須聲明,其中最后的數(shù)字部分是 Gradle 插件的版本號(hào)劣砍,到這里我們工程目錄下的 build.gradle 就分析完了惧蛹,通常情況下,這個(gè)文件中的內(nèi)容不需要修改刑枝,但是當(dāng)我們模塊化開發(fā)的時(shí)候香嗓,也可以在這里配置全局項(xiàng)目的構(gòu)建相關(guān)的配置。
3. app 目錄下的 build.gradle
// 表明是應(yīng)用程序的插件
apply plugin: 'com.android.application'
// Android 閉包
android {
compileSdkVersion 28 // 指定項(xiàng)目的編譯版本
defaultConfig {
applicationId "com.example.chenguiyan.justtest" // 指定項(xiàng)目的包名
minSdkVersion 15 // 指定項(xiàng)目最低兼容的安卓版本
targetSdkVersion 28 // 指定項(xiàng)目目標(biāo)兼容的安卓版本
versionCode 1 // 指定項(xiàng)目的版本號(hào)
versionName "1.0" // 指定項(xiàng)目的版本名
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// 指定是否對(duì)項(xiàng)目的代碼進(jìn)行混淆(true:混淆 false:不混淆)
minifyEnabled false
// proguardFiles 用于指定混淆時(shí)使用的規(guī)則文件
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
首先第一行應(yīng)用了一個(gè)插件装畅,這里一般有兩種值可選 ↓
- com.android.application: 表示是一個(gè)應(yīng)用程序模塊(可以直接運(yùn)行)
- com.android.library: 表示是一個(gè)庫(kù)模塊(只能作為代碼庫(kù)依附于別的應(yīng)用程序模塊來(lái)運(yùn)行)
接下來(lái)是一個(gè) android 閉包靠娱,在這個(gè)閉包中可以配置項(xiàng)目構(gòu)建的各種屬性,其中 compileSdkVersion 用于指定項(xiàng)目的編譯版本掠兄。
在 android 閉包中有嵌套了一個(gè) defaultConfig 閉包像云,defaultConfig 閉包中可以對(duì)項(xiàng)目的更多細(xì)節(jié)進(jìn)行配置,其中蚂夕,applicationId 用于指定項(xiàng)目的包名迅诬,其實(shí)我們?cè)陧?xiàng)目創(chuàng)建的時(shí)候已經(jīng)進(jìn)行過(guò)指定,如果想后來(lái)進(jìn)行修改婿牍,就可以在這里進(jìn)行修改侈贷,minSdkVersion 用于指定項(xiàng)目的最低兼容 Android 系統(tǒng)版本,這里指定的是 15 表示最低兼容到 Android 4.0 系統(tǒng)等脂,targetSdkVersion 指定的值表示你在該目標(biāo)版本上已經(jīng)做過(guò)充分的測(cè)試俏蛮,系統(tǒng)將會(huì)為你的應(yīng)用程序啟用最新的功能和特性,例如 android 6.0 系統(tǒng)中引入了運(yùn)行時(shí)權(quán)限這個(gè)功能上遥,如果你將 targetSdkVersion 指定成 23 或更高搏屑,那么系統(tǒng)就會(huì)為你的程序啟用運(yùn)行時(shí)權(quán)限功能,而如果你將 targetSdkVersion 指定成 22粉楚,那么就說(shuō)明你的程序最高只能在 Android 5.1 系統(tǒng)上做過(guò)充分的測(cè)試辣恋,Android 6.0 系統(tǒng)中引入的新功能自然就不會(huì)啟用了。剩下的兩個(gè)屬性 versionCode 用于指定項(xiàng)目的版本號(hào),versionName 用于指定項(xiàng)目的版本名抑党,這兩個(gè)屬性在生成安裝包的時(shí)候特別重要包警。
分析完 defaultConfig 閉包,我們接著來(lái)分析 buildTypes 閉包底靠,buildTypes 閉包中用于指定生成安裝文件的相關(guān)配置害晦,通常會(huì)有兩個(gè)子閉包,一個(gè)是 debug暑中,一個(gè)是 release壹瘟。debug 閉包用于指定生成測(cè)試版安裝文件的配置,release 閉包用于指定生成正式版安裝文件的配置鳄逾,另外 debug 閉包是可以忽略不寫的稻轨,因此我們看到上面的代碼中就只有一個(gè) release 閉包。接著我們看一下 release 閉包中的內(nèi)容雕凹,minifyEnabled 用于指定是否對(duì)項(xiàng)目的代碼進(jìn)行混淆殴俱,true 表示混淆,false 表示不混淆枚抵,proguardFiles 用于指定混淆時(shí)使用的規(guī)則文件线欲,這里可以指定兩個(gè)文件,第一個(gè) proguard-android.txt 是在 Android SDK 目錄下的汽摹,里面是所有項(xiàng)目通用的混淆規(guī)則李丰,第二個(gè) proguard-rules.pro 是當(dāng)前項(xiàng)目的根目錄下的,里面可以編寫當(dāng)前項(xiàng)目特有的混淆規(guī)則逼泣,需要注意的是趴泌,通過(guò) Android Studio 直接運(yùn)行的項(xiàng)目生成的都是測(cè)試版安裝文件。
下面還有一個(gè) dependencies 閉包拉庶,這個(gè)閉包的功能非常強(qiáng)大嗜憔,它主要是用來(lái)指定當(dāng)前項(xiàng)目所有的依賴關(guān)系,通常 Android Studio 項(xiàng)目一共有 3 種依賴方式:本地依賴砍的,遠(yuǎn)程依賴和庫(kù)依賴痹筛。本地依賴可以對(duì)本地的 Jar 包或目錄添加依賴關(guān)系莺治,遠(yuǎn)程依賴則可以對(duì) jcenter 庫(kù)上的開源項(xiàng)目添加依賴關(guān)系廓鞠,庫(kù)依賴可以對(duì)項(xiàng)目中的庫(kù)模塊添加依賴關(guān)系。implementation fileTree 就是一個(gè)本地依賴聲明谣旁,它表示將 libs 目錄下所有 .jar 后綴的文件都添加項(xiàng)目的構(gòu)建路徑中床佳,而 com.android.support:appcompat-v7:28.0.0 就是一個(gè)標(biāo)準(zhǔn)的遠(yuǎn)程依賴庫(kù)格式,其中 com.android.support 是域名部分榄审,用于和其他公司的庫(kù)做區(qū)分砌们,appcompat-v7 是組名稱,用于和同一個(gè)公司中不同的庫(kù)做區(qū)分,28.0.0 是版本號(hào)浪感,用于和同一個(gè)庫(kù)的不同版本做區(qū)分昔头,加上這句聲明后,Gradle 在構(gòu)建項(xiàng)目時(shí)會(huì)首先檢查一下本地是否已經(jīng)有這個(gè)庫(kù)的緩存影兽,如果沒有的話則會(huì)去自動(dòng)聯(lián)網(wǎng)下載揭斧,然后再添加到項(xiàng)目的構(gòu)建路徑中。而庫(kù)依賴聲明的基本格式是 implementation project 后面加上要依賴的庫(kù)名稱峻堰,另外的 testCompile 是用于聲明測(cè)試用例庫(kù)的讹开。
Gradle 多項(xiàng)目構(gòu)建
定義
前面的內(nèi)容講解只是基于創(chuàng)建項(xiàng)目后自動(dòng)生成的文件,整個(gè)工程里的 module 也只有 app 這個(gè)文件夾捐名。而現(xiàn)實(shí)情況里旦万,中大型的安卓軟件開發(fā) 為了提高可維護(hù)性和防止緊密耦合,可以基于特定的功能和邏輯將代碼寫在不同的模塊中镶蹋。模塊通常具有層次結(jié)構(gòu)而且可以定義為相互依賴成艘。Gradle對(duì)構(gòu)建多模塊項(xiàng)目提供了強(qiáng)大的支持,Gradle中的每一模塊都是一個(gè)項(xiàng)目贺归,我們稱之為多項(xiàng)目構(gòu)建狰腌。
注意:在Android Studio中,一個(gè)模塊和一個(gè)項(xiàng)目是有區(qū)別的牧氮。一個(gè)項(xiàng)目包含多個(gè)模塊module琼腔。而Android Studio中的每一個(gè)module對(duì)應(yīng)的是Gradle多項(xiàng)目構(gòu)建中的一個(gè)項(xiàng)目。
步驟
假若我們現(xiàn)在要構(gòu)建一個(gè)多項(xiàng)目工程踱葛,文件夾目錄如下 ↓
rootProject
|———— settings.gradle
|———— build.gradle
|———— sub_project1
| |———— build.gradle
|———— sub_project2
| |———— build.gradle
- 在多項(xiàng)目構(gòu)建中丹莲,根目錄下的 settings.gradle 文件聲明了所需的配置來(lái)實(shí)例化項(xiàng)目的層次結(jié)構(gòu)。該腳本的執(zhí)行是在構(gòu)建生命周期的初始化階段尸诽。Gradle 組裝構(gòu)建之前會(huì)創(chuàng)建一個(gè) Settings 類型的實(shí)例甥材。Settings 接口是 settings 文件的直接表示。若想使每個(gè)子項(xiàng)目都稱為構(gòu)建的一部分性含,則可以調(diào)用 Settings 接口中帶有項(xiàng)目路徑參數(shù)的 include 方法洲赵。
include ':sub_project1', ':sub_project2'
初始化階段,gradle 會(huì)尋找到這個(gè) settings.gradle 文件商蕴。如果該文件不存在叠萍,那么 gradle 就會(huì)假定你只有一個(gè)單獨(dú)的構(gòu)建模塊。如果你有多個(gè)模塊绪商,且 settings.gradle 文件定義了這些模塊的位置苛谷。如果這些子目錄包含了其自己的 build.gradle 文件,gradle 將會(huì)運(yùn)行它們格郁,并且將他們合并到構(gòu)建任務(wù)中腹殿。
- 之后在根目錄下的 build.gradle 里定義子模塊行為以及描述項(xiàng)目的一些公共插件独悴、屬性、依賴等锣尉。
方法名 | 描述 |
---|---|
allprojects | 配置當(dāng)前模塊以及所有子模塊行為 |
subprojects | 配置所有子模塊行為 |
project | 配置指定子模塊行為 |
舉個(gè)栗子 ↓
// 所有模塊都采用統(tǒng)一的版本號(hào)以及groupName
allprojects {
group = 'org.pkaq.gradle.multi'
version = "0.1.0"
}
// 為所有子模塊都應(yīng)用java插件
subprojects {
apply plugin: 'java'
}
// 為 sub_project1 模塊定義特定行為,采用war插件并且依賴base模塊
project(':sub_project1') {
apply plugin: 'war'
dependencies {
compile project(':base')
}
}
- 分離配置:當(dāng)項(xiàng)目足夠復(fù)雜的時(shí)候刻炒,采用集中化配置顯然不是一個(gè)好計(jì)謀。此時(shí)自沧,將不同項(xiàng)目的定制行為分離到各自的腳本里無(wú)疑會(huì)顯著的減輕根目錄 build.gradle 的負(fù)擔(dān)落蝙。這不僅可以是項(xiàng)目結(jié)構(gòu)看起來(lái)更加清晰,腳本更加易讀暂幼,分離化的配置還可以讓我們更好地關(guān)注子模塊的特定行為筏勒。要分離模塊配置只需要很簡(jiǎn)單的兩步:
- 在子模塊建立 build.gradle 文件。
- 將原來(lái)的 project(':xxx'){} 內(nèi)的腳本移動(dòng)到上面建立的文件中去旺嬉。