一個(gè)Gradle項(xiàng)目的構(gòu)建過程定義在build.gradle文件中,位于項(xiàng)目的根目錄下。
一個(gè)最簡單的Gradle純Java項(xiàng)目的build.gradle文件包含以下內(nèi)容:
apply plugin: 'java'
這里引入了Gradle的Java插件。這個(gè)插件提供了所有構(gòu)建和測試Java應(yīng)用程序所需要的東西。
最簡單的Android項(xiàng)目的build.gradle文件包含以下內(nèi)容:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.11.1'
}
}
apply plugin: 'android'
android {
compileSdkVersion 19
buildToolsVersion "19.0.0"
}
這里包括了Android build file的3個(gè)主要部分:
buildscrip{...}
這里配置了驅(qū)動(dòng)構(gòu)建過程的代碼。
在這個(gè)部分,它聲明了使用Maven倉庫,并且聲明了一個(gè)maven文件的依賴路徑。這個(gè)文件就是包含了0.11.1版本android gradle插件的庫。
注意:這里的配置只影響控制構(gòu)建過程的代碼,不影響項(xiàng)目源代碼。項(xiàng)目本身需要聲明自己的倉庫和依賴關(guān)系,稍后將會(huì)提到這部分。
接下來,跟前面提到的Java Plugin一樣添加了android
plugin。
最后,andorid{...}
配置了所有android構(gòu)建過程需要的參數(shù)。這里也是Android DSL的入口點(diǎn)。
默認(rèn)情況下,只需要配置目標(biāo)編譯SDK版本和編譯工具版本,即compileSdkVersion
和buildToolsVersion
屬性。這個(gè)complieSdkVersion
屬性相當(dāng)于舊構(gòu)建系統(tǒng)中project.properites文件中的target
屬性。這個(gè)新的屬性可以跟舊的target
屬性一樣指定一個(gè)int或者String類型的值。
重要:
你只能添加android
plugin。同時(shí)添加java
plugin會(huì)導(dǎo)致構(gòu)建錯(cuò)誤。注意:
你同樣需要在相同路徑下添加一個(gè)local.properties文件,并使用sdk.dir屬性來設(shè)置SDK路徑。 另外,你也可以通過設(shè)置ANDROID_HOME
環(huán)境變量,這兩種方式?jīng)]有什么不同,根據(jù)你自己的喜好選擇其中一種設(shè)置。
上面提到的基本的構(gòu)建文件需要一個(gè)默認(rèn)的文件夾結(jié)構(gòu)。Gradle遵循約定優(yōu)先于配置的概念,在可能的情況盡可能提供合理的默認(rèn)配置參數(shù)。
基本的項(xiàng)目開始于兩個(gè)名為“source sets”的組件,即main source code和test code。它們分別位于:
里面每一個(gè)存在的文件夾對應(yīng)相應(yīng)的源組件。對于Java plugin和Android plugin來說,它們的Java源代碼和資源文件路徑如下:
但對于Android plugin來說,它還擁有以下特有的文件和文件夾結(jié)構(gòu):
注意:
src/androidTest/AndroidManifest.xml是不需要的,它會(huì)自動(dòng)被創(chuàng)建。
當(dāng)默認(rèn)的項(xiàng)目結(jié)構(gòu)不適用的時(shí)候,你可能需要去配置它。根據(jù)Gradle文檔,重新為Java項(xiàng)目配置sourceSets可以使用以下方法:
sourceSets {
main {
java {
srcDir 'src/java'
}
resources {
srcDir 'src/resources'
}
}
}
注意:
srcDir
將會(huì)被添加到指定的已存在的源文件夾中(這在Gradle文檔中沒有提到,但是實(shí)際上確實(shí)會(huì)這樣執(zhí)行)。
替換默認(rèn)的源代碼文件夾,你可能想要使用能夠傳入一個(gè)路徑數(shù)組的srcDirs
來替換單一的srcDir
。以下是使用調(diào)用對象的另一種不同方法:
sourceSets {
main.java.srcDirs = ['src/java']
main.resources.srcDirs = ['src/resources']
}
想要獲取更多信息,可以參考Gradle文檔中關(guān)于Java Pluign的部分。
Android Plugin使用的是類似的語法。但是由于它使用的是自己的sourceSets,這些配置將會(huì)被添加在android
對象中。
以下是一個(gè)示例,它使用了舊項(xiàng)目結(jié)構(gòu)中的main源碼,并且將androidTest
sourceSet組件重新映射到tests文件夾。
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
androidTest.setRoot('tests')
}
}
注意:
由于舊的項(xiàng)目結(jié)構(gòu)將所有的源文件(java,aidl,renderscripthe和java資源文件)都放在同一個(gè)目錄里面,所以我們需要將這些sourceSet組件重新映射到src
目錄下。注意:
setRoot()
方法將移動(dòng)整個(gè)組件(包括它的子文件夾)到一個(gè)新的文件夾。示例中將會(huì)移動(dòng)src/androidTest/
到tests/
下。
以上這些是Android特有的,如果配置在Java的sourceSets里面將不會(huì)有作用。
以上也是將舊構(gòu)建系統(tǒng)項(xiàng)目遷移到新構(gòu)建系統(tǒng)需要做的遷移工作。
添加一個(gè)插件到構(gòu)建文件中將會(huì)自動(dòng)創(chuàng)建一系列構(gòu)建任務(wù)(build tasks)去執(zhí)行(注:gradle屬于任務(wù)驅(qū)動(dòng)型構(gòu)建工具,它的構(gòu)建過程是基于Task的)。Java plugin和Android plugin都會(huì)創(chuàng)建以下task:
assemble
這個(gè)task將會(huì)組合項(xiàng)目的所有輸出。
check
這個(gè)task將會(huì)執(zhí)行所有檢查。
build
這個(gè)task將會(huì)執(zhí)行assemble和check兩個(gè)task的所有工作
clean
這個(gè)task將會(huì)清空項(xiàng)目的輸出。
實(shí)際上assemble
,check
,build
這三個(gè)task不做任何事情。它們只是一個(gè)Task標(biāo)志,用來告訴android plugin添加實(shí)際需要執(zhí)行的task去完成這些工作。
這就允許你去調(diào)用相同的task,而不需要考慮當(dāng)前是什么類型的項(xiàng)目,或者當(dāng)前項(xiàng)目添加了什么plugin。例如,添加了findbugs plugin將會(huì)創(chuàng)建一個(gè)新的task并且讓check
task依賴于這個(gè)新的task。當(dāng)check
task被調(diào)用的時(shí)候,這個(gè)新的task將會(huì)先被調(diào)用。
在命令行環(huán)境中,你可以執(zhí)行以下命令來獲取更多高級(jí)別的task:
gradle tasks
查看所有task列表和它們之間的依賴關(guān)系可以執(zhí)行以下命令:
gradle tasks --all
注意:
Gradle會(huì)自動(dòng)監(jiān)視一個(gè)task聲明的所有輸入和輸出。
兩次執(zhí)行build
task并且期間項(xiàng)目沒有任何改動(dòng),gradle將會(huì)使用UP-TO-DATE通知所有task。這意味著第二次build執(zhí)行的時(shí)候不會(huì)請求任何task執(zhí)行。這允許task之間互相依賴,而不會(huì)導(dǎo)致不需要的構(gòu)建請求被執(zhí)行。
Java plugin主要?jiǎng)?chuàng)建了兩個(gè)task,依賴于main task(一個(gè)標(biāo)識(shí)性的task):
assemble
jar
這個(gè)task創(chuàng)建所有輸出
check
test
jar
task自身直接或者間接依賴于其他task:classes
task將會(huì)被調(diào)用于編譯java源碼。testClasses
task用于編譯測試,但是它很少被調(diào)用,因?yàn)?code>test task依賴于它(類似于classes
task)。
通常情況下,你只需要調(diào)用到assemble
和check
,不需要其他task。
你可以在Gradle文檔中查看java plugin的全部task。
Android plugin使用相同的約定以兼容其他插件,并且附加了自己的標(biāo)識(shí)性task,包括:
assemble
check
connectedCheck
deviceCheck
build
clean
這些新的標(biāo)識(shí)性task是必須的,以保證能夠在沒有設(shè)備連接的情況下執(zhí)行定期檢查。注意build
task不依賴于deviceCheck
或者connectedCheck
。
一個(gè)Android項(xiàng)目至少擁有兩個(gè)輸出:debug APK(調(diào)試版APK)和release APK(發(fā)布版APK)。每一個(gè)輸出都擁有自己的標(biāo)識(shí)性task以便能夠單獨(dú)構(gòu)建它們。
assemble
assembleDebug
assembleRelease
它們都依賴于其它一些tasks以完成構(gòu)建一個(gè)APK需要多個(gè)步驟。其中assemble task依賴于這兩個(gè)task,所以執(zhí)行assemble
將會(huì)同時(shí)構(gòu)建出兩個(gè)APK。
小提示:
gradle在命令行終端上支持駱駝命名法的task簡稱,例如,執(zhí)行gradle aR
命令等同于執(zhí)行gradle assembleRelease
check task也擁有自己的依賴:
check
lint
connectedCheck
connectedAndroidTest
connectedUiAutomatorTest
(目前還沒有應(yīng)用到)
deviceCheck
最后,只要task能夠被安裝(那些要求簽名的task),android plugin就會(huì)為所有構(gòu)建類型(debug
,release
,test
)安裝或者卸載。
Android plugin提供了大量DSL用于直接從構(gòu)建系統(tǒng)定制大部分事情。
通過SDL可以配置一下manifest選項(xiàng):
例如:
android {
compileSdkVersion 19
buildToolsVersion "19.0.0"
defaultConfig {
versionCode 12
versionName "2.0"
minSdkVersion 16
targetSdkVersion 16
}
}
在android
元素中的defaultConfig
元素中定義所有配置。
之前的Android Plugin版本使用packageName來配置manifest文件中的packageName屬性。從0.11.0版本開始,你需要在build.gradle文件中使用applicationId來配置manifest文件中的packageName屬性。這是為了消除應(yīng)用程序的packageName(也是程序的ID)和java包名所引起的混亂。
在構(gòu)建文件中定義的強(qiáng)大之處在于它是動(dòng)態(tài)的。例如,可以從一個(gè)文件中或者其它自定義的邏輯代碼中讀取版本信息:
def computeVersionName() {
...
}
android {
compileSdkVersion 19
buildToolsVersion "19.0.0"
defaultConfig {
versionCode 12
versionName computeVersionName()
minSdkVersion 16
targetSdkVersion 16
}
}
注意:
不要使用與在給定范圍內(nèi)的getter方法可能引起沖突的方法名。例如,在defaultConfig{...}中調(diào)用getVersionName()將會(huì)自動(dòng)調(diào)用defaultConfig.getVersionName()方法,你自定義的getVersionName()方法就被取代掉了。
如果一個(gè)屬性沒有使用DSL進(jìn)行設(shè)置,一些默認(rèn)的屬性值將會(huì)被使用。以下表格是可能使用到的值:
Property Name | Default value in DSL object | Default value |
---|---|---|
versionCode | -1 | value from manifest if present |
versionName | null | value from manifest if present |
minSdkVersion | -1 | value from manifest if present |
targetSdkVersion | -1 | value from manifest if present |
applicationId | null | value from manifest if present |
testApplicationId | null | applicationId + “.test” |
testInstrumentationRunner | null | android.test.InstrumentationTestRunner |
signingConfig | null | null |
proguardFile | N/A (set only) | N/A (set only) |
proguardFiles | N/A (set only) | N/A (set only) |
如果你在構(gòu)建腳本中使用自定義代碼邏輯請求這些屬性,那么第二列的值將非常重要。例如,你可能會(huì)寫:
if (android.defaultConfig.testInstrumentationRunner == null) {
// assign a better default...
}
如果這個(gè)值一直保持null,那么在構(gòu)建執(zhí)行期間將會(huì)實(shí)際替換成第三列的默認(rèn)值。但是在DSL元素中并沒有包含這個(gè)默認(rèn)值,所以,你無法查詢到這個(gè)值。
除非是真的需要,這是為了預(yù)防解析應(yīng)用的manifest文件。
默認(rèn)情況下,Android Plugin會(huì)自動(dòng)給項(xiàng)目設(shè)置同時(shí)構(gòu)建應(yīng)用程序的debug和release版本。兩個(gè)版本之間的不同主要圍繞著能否在一個(gè)安全設(shè)備上調(diào)試,以及APK如何簽名。
Debug版本采用使用通用的name/password鍵值對自動(dòng)創(chuàng)建的數(shù)字證書進(jìn)行簽名,以防止構(gòu)建過程中出現(xiàn)請求信息。Release版本在構(gòu)建過程中沒有簽名,需要稍后再簽名。
這些配置通過一個(gè)BuildType
對象來配置。默認(rèn)情況下,這兩個(gè)實(shí)例都會(huì)被創(chuàng)建,分別是一個(gè)debug
版本和一個(gè)release
版本。
Android plugin允許像創(chuàng)建其他構(gòu)建類型一樣定制debug
和release
實(shí)例。這需要在buildTypes
的DSL容器中配置:
android {
buildTypes {
debug {
applicationIdSuffix ".debug"
}
jnidebug.initWith(buildTypes.debug)
jnidebug {
packageNameSuffix ".jnidebug"
jnidebugBuild true
}
}
}
以上代碼片段實(shí)現(xiàn)了以下功能:
配置默認(rèn)的debug
構(gòu)建類型
將debug版本的包名設(shè)置為.debug以便能夠同時(shí)在一臺(tái)設(shè)備上安裝debug和release版本的apk。
jnidebug
的新構(gòu)建類型,并且這個(gè)構(gòu)建類型是debug
構(gòu)建類型的一個(gè)副本。jnidebug
構(gòu)建類型,允許使用JNI組件,并且也添加了不一樣的包名后綴。創(chuàng)建一個(gè)新的構(gòu)建類型就是簡單的在buildType
標(biāo)簽下添加一個(gè)新的元素,并且可以使用initWith()
或者直接使用閉包來配置它。
以下是一些可能使用到的屬性和默認(rèn)值:
Property name | Default values for debug | Default values for release / other |
---|---|---|
debuggable | true | false |
jniDebugBuild | false | false |
renderscriptDebugBuild | false | false |
renderscriptOptimLevel | 3 | 3 |
applicationIdSuffix | null | null |
versionNameSuffix | null | null |
signingConfig | android.signingConfigs.debug | null |
zipAlign | false | true |
runProguard | false | false |
proguardFile | N/A (set only) | N/A (set only) |
proguardFiles | N/A (set only) | N/A (set only) |
除了以上屬性之外,Build Type還會(huì)受項(xiàng)目源碼和資源影響:對于每一個(gè)Build Type都會(huì)自動(dòng)創(chuàng)建一個(gè)匹配的sourceSet。默認(rèn)的路徑為:
src/<buildtypename>/
這意味著BuildType名稱不能是main或者androidTest(因?yàn)檫@兩個(gè)是由plugin強(qiáng)制實(shí)現(xiàn)的),并且他們互相之間都必須是唯一的。
跟其他sourceSet設(shè)置一樣,Build Type的source set路徑可以重新被定向:
android {
sourceSets.jnidebug.setRoot('foo/jnidebug')
}
另外,每一個(gè)Build Type都會(huì)創(chuàng)建一個(gè)新的assemble任務(wù)。
assembleDebug
和assembleRelease
兩個(gè)Task在上面已經(jīng)提到過,這里要講這兩個(gè)Task從哪里被創(chuàng)建。當(dāng)debug
和release
構(gòu)建類型被預(yù)創(chuàng)建的時(shí)候,它們的tasks就會(huì)自動(dòng)創(chuàng)建對應(yīng)的這個(gè)兩個(gè)Task。
上面提到的build.gradle代碼片段中也會(huì)實(shí)現(xiàn)assembleJnidebug
task,并且assemble
會(huì)像依賴于assembleDebug
和assembleRelease
一樣依賴于assembleJnidebug
。
提示:你可以在終端下輸入gradle aJ去運(yùn)行assembleJnidebug task
。
可能會(huì)使用到的情況:
BuildType的代碼和資源通過以下方式被使用:
對一個(gè)應(yīng)用程序簽名需要以下:
位置,鍵名,兩個(gè)密碼,還有存儲(chǔ)類型一起形成了簽名配置。
默認(rèn)情況下,debug
被配置成使用一個(gè)debug keystory。debug keystory使用了默認(rèn)的密碼和默認(rèn)key及默認(rèn)的key密碼。debug keystory的位置在$HOME/.android/debug.keystroe,如果對應(yīng)位置不存在這個(gè)文件將會(huì)自動(dòng)創(chuàng)建一個(gè)。
debug
Build Type(構(gòu)建類型) 會(huì)自動(dòng)使用debug
SigningConfig (簽名配置)。
可以創(chuàng)建其他配置或者自定義內(nèi)建的默認(rèn)配置。通過signingConfigs
這個(gè)DSL容器來配置:
android {
signingConfigs {
debug {
storeFile file("debug.keystore")
}
myConfig {
storeFile file("other.keystore")
storePassword "android"
keyAlias "androiddebugkey"
keyPassword "android"
}
}
buildTypes {
foo {
debuggable true
jniDebugBuild true
signingConfig signingConfigs.myConfig
}
}
}
以上代碼片段修改debug keystory的路徑到項(xiàng)目的根目錄下。在這個(gè)例子中,這將自動(dòng)影響其他使用到debug
構(gòu)建類型的構(gòu)建類型。
這里也創(chuàng)建了一個(gè)新的Single Config
(簽名配置)和一個(gè)使用這個(gè)新簽名配置的新的Build Type
(構(gòu)建類型)。
注意:只有默認(rèn)路徑下的debug keystory不存在時(shí)會(huì)被自動(dòng)創(chuàng)建。更改debug keystory的路徑并不會(huì)自動(dòng)在新路徑下創(chuàng)建debug keystory。如果創(chuàng)建一個(gè)新的不同名字的SignConfig,但是使用默認(rèn)的debug keystore路徑來創(chuàng)建一個(gè)非默認(rèn)的名字的SigningConing,那么還是會(huì)在默認(rèn)路徑下創(chuàng)建debug keystory。換句話說,會(huì)不會(huì)自動(dòng)創(chuàng)建是根據(jù)keystory的路徑來判斷,而不是配置的名稱。
注意:雖然經(jīng)常使用項(xiàng)目根目錄的相對路徑作為keystore的路徑,但是也可以使用絕對路徑,盡管這并不推薦(除了自動(dòng)創(chuàng)建出來的debug keystore)。
注意:如果你將這些文件添加到版本控制工具中,你可能不希望將密碼直接寫到這些文件中。下面Stack Overflow鏈接提供從控制臺(tái)或者環(huán)境變量中獲取密碼的方法:
http://stackoverflow.com/questions/18328730/how-to-create-a-release-signed-apk-file-using-gradle
我們以后還會(huì)在這個(gè)指南中添加更多的詳細(xì)信息。
從Gradle Plugin for ProGuard version 4.10之后就開始支持ProGuard。ProGuard插件是自動(dòng)添加進(jìn)來的。如果Build Type的runProguard屬性被設(shè)置為true,對應(yīng)的task將會(huì)自動(dòng)創(chuàng)建。
android {
buildTypes {
release {
runProguard true
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
productFlavors {
flavor1 {
}
flavor2 {
proguardFile 'some-other-rules.txt'
}
}
}
發(fā)布版本將會(huì)使用它的Build Type中聲明的規(guī)則文件,product flavor(定制的產(chǎn)品版本)將會(huì)使用對應(yīng)flavor中聲明的規(guī)則文件。
這里有兩個(gè)默認(rèn)的規(guī)則文件:
這兩個(gè)文件都在SDK的路徑下。使用getDefaultProguardFile()可以獲取這些文件的完整路徑。它們除了是否要進(jìn)行優(yōu)化之外,其它都是相同的。
你能夠在編譯的時(shí)候自動(dòng)化地移除不用的資源。更多的信息,請看文檔 Resource Shrinking
更多建議: