轉(zhuǎn)載注明出處:http://www.itdecent.cn/p/5255b100930e
0. 前言
完全由個人翻譯,能力有限,有些細節(jié)地方翻譯不是很通順,大家可以參考Gradle Plugin User Guide英文版本閱讀,如果有問題,歡迎指正。
譯文git工程:https://github.com/Kyogirante/gradle_user_guide,歡迎star。
轉(zhuǎn)載請事先溝通,未經(jīng)允許,謝絕轉(zhuǎn)載。
1. 新工具介紹(Introduction)
- 能夠復用代碼和資源
- 能夠構(gòu)建幾種不同版本參數(shù)的應用
- 能夠配置、擴展、自定義構(gòu)建過程
1.1 為什么選擇Gradle(Why Gradle?)
Gradle是一款具有優(yōu)勢的構(gòu)建工具,通過插件可以自定義構(gòu)建過程。主要優(yōu)勢如下:
- 基于Groovy的領域特定語言(DSL),用于描述和操作構(gòu)建過程
- 支持maven/lvy的依賴管理
- 非常靈活,并不強迫用戶一定要使用最佳的構(gòu)建方式
- 插件可以暴露自身的語言和接口api給構(gòu)建文件使用
- 支持IDE集成
2.2 需求(Requirements)
- Gradle 2.2(Gradle版本是2.2及以上,因為文檔中有些新特性)
- SDK with Build Tools 19.0.0.
2. 工程基本配置(Basic Project Setup)
Gradle工程默認的配置文件名稱是build.gradle,在主工程的根目錄下。
2.1 配置文件示例(Simple build files)
下面是一個Android工程的最簡單配置文件的內(nèi)容。
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
}
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.1.0"
}
配置內(nèi)容主要分為三部分。
-
buildscript{},這個部分主要配置在構(gòu)建過程中的依賴。上面示例中,聲明使用jcenter依賴庫,聲明了一個maven庫的依賴com.android.tools.build:gradle:1.3.1,是指引入gradle集成工具,版本是1.3.1。(關于Android Gradle Plugin版本和Gradle版本關系,點這里) -
apply plugin,引用插件,com.android.application這個插件用于構(gòu)建Android工程 -
android {},這部分是配置構(gòu)建Android工程的參數(shù)。compileSdkVersion和buildToolsVersion是必須的
注意:主工程中只能引用com.android.application插件,如果引用java插件會報錯,參考這里,第一個插件說明這是個Android工程,第二個插件說明這是個Java工程,所以只能引用一個。
注意:用戶可以在local.properties文件中使用sdk.dir屬性配置本地的Android sdk位置,或者設置一個名為Android_HOME的環(huán)境變量,這兩種方法沒有什么區(qū)別。
示例local.properties:
sdk.dir=/path/to/Android/Sdk
2.2 工程結(jié)構(gòu)(Project Structure)
Android工程文件有默認的目錄結(jié)構(gòu)。Gradle遵循約定由于配置規(guī)則,提供合理的默認值。工程以兩個目錄為主,一個是工程代碼目錄,一個是測試代碼目錄。
- src/main/
- src/androidTest/
在每個目錄中都有一些子目錄,Java工程和Android工程共有的子目錄如下:
- java/
- resources/
Android工程中有一些獨有的目錄:
- AndroidManifest.xml
- res
- assets
- aidl
- rs
- jni
- jniLibs
所有的java文件都在src/main/java目錄下,主要的配置文件目錄是src/main/AndroidManifest.xml。
src/main/AndroidManifest.xml是自動創(chuàng)建的,不需要手動創(chuàng)建
2.2.1 配置目錄結(jié)構(gòu)(Configuring the Structure)
默認的目錄結(jié)構(gòu)并不能完全適配所有情況,用戶可以配置目錄結(jié)構(gòu)。點擊這里查看Java工程師怎么配置目錄結(jié)構(gòu)的。
在Android工程中使用同樣的格式,但是因為Android工程中有獨有的一些目錄,所以配置信息需要寫在android {}這部分。下面示例中,工程代碼使用原來的目錄,修改測試代碼的目錄。
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')
}
}
注意:因為舊的目錄中包含所有的文件(java、AIDL、res等等),所以需要重設所有的目錄
注意:setRoot()重設目錄位置,沿用之前的目錄結(jié)構(gòu),這個是Android工程特有的
2.3 構(gòu)建任務(Build Tasks)
2.3.1 通用任務(General Tasks)
使用插件(包含Java和Android插件)去構(gòu)建工程會自動創(chuàng)建很多任務,通用的任務如下:
- assemble,打包工程所產(chǎn)出的文件
- check,運營工程中所有的check任務
- build, 執(zhí)行assemble任務和check任務
- clean,清除工程的產(chǎn)出的文件
assemble、check、build這三個任務實際上并不做任何事,他們只是一個殼任務,實告訴Gradle去執(zhí)行那些的任務。
不管什么工程,依賴了什么插件,都可以反復去調(diào)用同一個任務。例如引用一個findBugs插件,會創(chuàng)建一個新的任務,讓check任務依賴這個新任務,這樣每次調(diào)用check任務時候,新建的任務也會執(zhí)行。
- 使用
gradle tasks指令獲取工程中所有的可執(zhí)行任務 - 使用
gradle tasks --all執(zhí)行獲取工程中所有可執(zhí)行任務簡介以及依賴關系
如果工程中未做任何修改,執(zhí)行build任務,每個任務描述后面都會加上UP-TO-DATE,這意味著這個任務不需要真正地執(zhí)行,因為工程沒有改動。這樣每個任務都可以依賴其他任務,而且不需要其他任務做構(gòu)建工作。
2.3.2 Java工程任務(Java project tasks)
引用Java插件時候,說明這個工程是個純Java工程,會額外添加兩個殼任務jar和tests。
- assemble
- jar 打包工程產(chǎn)出文件
- check
- tests 執(zhí)行所有測試
jar任務會直接或者間接的依賴任務classes,這個任務會編譯java源代碼;tests任務會依賴任務testClasses,但是很少會直接調(diào)用這個任務,因為tests任務依賴它,直接調(diào)用tests任務即可。
大體上,用戶可能只會調(diào)用assemble和check任務,很少調(diào)用其他任務??梢?a target="_blank" rel="nofollow">點擊這里查看Java工程所有的任務和任務描述。
2.3.3 Android工程任務(Android tasks)
引用com.android.application插件,說明這個工程是Android工程,在通用任務基礎上會額外添加兩個殼任務。
- connectedCheck,查看是否有設備連接
- deviceCheck, 查看是否連接上設備
注意,build任務是不依賴connectedCheck和deviceCheck任務的。
一個Android工程至少有兩個構(gòu)建包,debug apk和release apk。每一個構(gòu)建包都有自己的殼任務。
- assemble
- assembleDebug
- assembleRelease
這兩個任務會依賴其他一些任務,要構(gòu)建出一個安裝包,需要執(zhí)行好多步驟。assemble任務依賴這兩個任務,所以執(zhí)行assemble任務時候,會產(chǎn)出debug和release兩個apk。
注意:Gradle支持指令簡寫模式,例如gradle aR和gradle assembleRelease意義是相同的,只需要保證沒有其他任務能簡寫成aR。
Android工程中check類任務有各自的依賴。
- check
- lint
- connectedCheck
- connectedAndroidTest
- deviceCheck
- 它依賴于那些擴展了
tests通用任務的任務
- 它依賴于那些擴展了
最后,Android工程中,也會有對程序安裝和卸載的任務。
- installDebug
- installRelease
- uninstallAll
- uninstallDebug
- uninstallRelease
- uninstallDebugAndroidTest
2.4 自定義基本構(gòu)建(Basic Build Customization)
Android的插件提供了領域特定語言(DSL)來幫助用戶直接地自定義構(gòu)建過程。
2.4.1 清單內(nèi)容(Manifest entries)
通過DSL用戶可以設置一些構(gòu)建參數(shù),可設置內(nèi)容如下:
- minSdkVersion
- targetSdkVersion
- versionCode
- versionName
- applicationId (最終有效的包名,點擊這里查看細節(jié))
- testApplicationId (用于測試app)
- testInstrumentationRunner
示例如下:
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
versionCode 12
versionName "2.0"
minSdkVersion 16
targetSdkVersion 23
}
}
點擊這里查看可以配置的清單參數(shù)信息。
可以在.gradle文件中動態(tài)配置這些清單信息,例如,動態(tài)配置versionName參數(shù),示例如下:
def computeVersionName() {
...
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
versionCode 12
versionName computeVersionName()
minSdkVersion 16
targetSdkVersion 23
}
}
注意:自定義時盡量不要使用gettter的方法名,防止沖突,例如,defaultConfig.getVersionName()會替換掉自定義的getVersionName()方法,也就是說每一個參數(shù)都有默認的getter方法
2.4.2 構(gòu)建類型(Build Types)
Android工程中默認的會有debug和release兩種構(gòu)建方式,主要區(qū)別在于調(diào)試程序的能力以及apk簽名細節(jié)。debug的版本為了防止在構(gòu)建過程中彈出提示,系統(tǒng)會根據(jù)明文的用戶名/密碼自動創(chuàng)建一個數(shù)字證書用于簽名,使用debug證書簽名的apk是無法上架銷售的。release版本在構(gòu)建過程中不進行簽名,將簽名放在之后的環(huán)節(jié)。
在buildTypes中配置構(gòu)建類型信息,默認會創(chuàng)建兩種構(gòu)建方式,debug和release,在Android工程中允許自定義這兩種構(gòu)建方式的具體細節(jié)信息。示例如下:
android {
buildTypes {
debug {
applicationIdSuffix ".debug"
}
jnidebug {
initWith(buildTypes.debug)
applicationIdSuffix ".jnidebug"
jniDebuggable true
}
}
}
上面代碼作用:
- 設置debug構(gòu)建類型的包名是
<app appliationId>.debug,這樣一臺設備上面就可以同時安裝debug和release的包,不會出現(xiàn)包名沖突情況 - 新建一個新的構(gòu)建類型,名為
jnidebug,initWith(buildTypes.debug)表示buildTypes.debug構(gòu)建類型(Build Type)配置信息應用到這個構(gòu)建中 - 重新設置包名同時設為
jniDebuggable為true,開啟debug模式
在buildTypes中新建一個新的構(gòu)建類型非常方便,可以使用initWith()復用其他構(gòu)建類型的構(gòu)建參數(shù)。點擊這個查看可配置的構(gòu)建參數(shù)。
除了修改構(gòu)建參數(shù)意外,buildTypes中還可以添加特定的代碼和資源。每一種構(gòu)建類型,默認都有一個特定資源目錄src/<build_type_name>/,例如src/debug/java目錄,只有構(gòu)建debug類型apk時候才會用到這個目錄下的資源。這就意味著構(gòu)建類型不能是main和androidTest,這兩個目錄是工程的默認目錄,參考上面2.2提到的目錄結(jié)構(gòu)。
跟上文提到的sourceSet一樣,每一種構(gòu)建類型可以重設目錄,示例如下:
android {
sourceSets.jnidebug.setRoot('foo/jnidebug')
}
另外,對于每一個構(gòu)建類型,都會有一個新的工程任務被創(chuàng)建,名為assemble<Build Type Name>,例如上文提到的assembleDebug和assembleRelease, 這兩個任務也是來源于此。
根據(jù)這個規(guī)則,上面配置信息就會產(chǎn)生assembleJnidebug新任務,assemble任務像依賴assembleDebug和assembleRelease任務一樣,也會依賴這個新任務。
可能新建構(gòu)建類型的場景:
- 某些權(quán)限/模式在debug才開啟,release版本不開啟
- 自定義debug調(diào)試實現(xiàn)
- debug模式需要一些額外的資源
構(gòu)建中設置的代碼/資源主要用于以下幾點:
- 合并到主清單
- 代碼實現(xiàn)替換
- 資源覆蓋
2.4.3 簽名信息配置(Signing Configurations)
應用簽名以下信息是必須的:
- A keystore
- A keystore password
- A key alias name
- A key password
- The store type
點擊這里查看Android官方簽名細節(jié)及具體過程。
點擊這里查看可配置的簽名信息,這些信息是在signingConfigs{}模塊中配置的。
Android工程中,debug構(gòu)建會用通用的debug.keysotre和密碼、通用的key和密碼,keystore文件位于$HOME/.android/debug.keystore這個目錄。
具體示例如下:
android {
signingConfigs {
debug {
storeFile file("debug.keystore")
}
myConfig {
storeFile file("other.keystore")
storePassword "android"
keyAlias "androiddebugkey"
keyPassword "android"
}
}
buildTypes {
foo {
signingConfig signingConfigs.myConfig
}
}
}
上述示例中聲明了兩個簽名類型signingConfigs.debug和 signingConfigs.myConfig,兩者都將設置了keystore位置,位于工程根目錄,同時
myCondig設置了其他必需信息,debug使用通用信息,不用配置。
注意:只有debug類型簽名的keystore位于默認位置,系統(tǒng)才會自動創(chuàng)建,如果重設了keystore的位置,就不會自動創(chuàng)建。新建簽名類型會自動使用默認的keystore,如果沒有,系統(tǒng)會自動創(chuàng)建,也就是說,系統(tǒng)是否自動創(chuàng)建keystore,是跟簽名類型的storeFile的位置有關系,跟簽名類型的名稱沒有關系。
注意:storeFile所以的目錄在工程的根目錄,是個相對目錄,當然也可以設置為絕對目錄,但是不推薦這樣做
注意:如果要根據(jù)具體情況來控制簽名參數(shù),就不能直接將key和密碼等信息直接寫在signingConfigs中,可以在gradle.properties文件中設置簽名具體細節(jié),然后在signingConfigs引用,具體點擊這里查看
3. 工程依賴/Android庫/多工程設置(Dependencies, Android Libraries and Multi-project setup)
Gradle工程可以依賴其他組件,這些組件可能是外部jar包也可能是一個Gradle工程。
3.1 依賴jar包(Dependencies on binary packages)
3.1.1 本庫jar包(Local packages)
依賴外部jar包,需要在.gradle文件中使用compile進行配置,示例如下:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
android {
...
}
注意:dependencies屬于標準Gradle API的DSL屬性,并不是屬于andorid{}的元素
compile屬性用于編譯整個工程,compile的庫都會被添加到編譯路徑中,最終也會打包到最終的apk中。依賴類型分為以下幾種:
- compile,主工程
- androidTestCompile,測試工程
- debugCompile, debug構(gòu)建類型
- releaseCompile, release構(gòu)建類型
因為構(gòu)建一個apk不可能沒有構(gòu)建類型,所以一般至少有兩個compile類型配置(compile和<buildtype>Compile)甚至更多。每創(chuàng)建一個新的構(gòu)建類型,系統(tǒng)都會自動基于構(gòu)建類型的名稱創(chuàng)建一個新的compile類型,名為<buildtype>Compile。這在構(gòu)建打包過程非常有用,例如debug版本需要某個外部庫而release版本不需要,又比如不同的構(gòu)建打包對同一個外部庫依賴的版本不同。點擊這里查看解決jar包版本沖突的具體信息。
3.1.2 遠程依賴(Remote artifacts)
Gradle支持從Maven/Lvy倉庫拉取依賴。首先聲明倉庫,然后要在聲明具體的依賴。示例如下:
repositories {
jcenter()
}
dependencies {
compile 'com.google.guava:guava:18.0'
}
android {
...
}
注意:jcenter()是一個倉庫URL的縮寫。Gradle支持遠端和本地倉庫。
注意:Gradle支持依賴傳遞,也就是說A工程依賴B,B依賴C,那么A工程也會依賴C,A工程會從倉庫中獲取C。
點擊這里查看更多的依賴設置細節(jié);點擊這里查看具體依賴設置語言示例。
3.2 多工程設置(Multi project setup)
通過多工程依賴設置,Gradle工程可以依賴其他的Gradle工程。多工程設置是通過將其他被依賴的Gradle工程放入主工程的子目錄中,如下結(jié)構(gòu):
MyProject/
+ app/
+ libraries/
+ lib1/
+ lib2/
這里面有三個Gradle工程,通過以下的方式能夠引用到具體的工程:
:app:libraries:lib1:libraries:lib2
每一個工程擁有自己的build.gradle文件,配置該工程的構(gòu)建細節(jié),目錄結(jié)構(gòu)如下:
MyProject/
| settings.gradle
+ app/
| build.gradle
+ libraries/
+ lib1/
| build.gradle
+ lib2/
| build.gradle
另外,在上面結(jié)構(gòu)中可以看見在主工程的根目錄中有一個settings.gradle文件,這個文件是用來定義那些目錄是Gradle工程,settings.gradle示例內(nèi)容如下,里面定義了三個Gradle工程目錄:
include ':app', ':libraries:lib1', ':libraries:lib2'
主工程:app想要依賴其他的Gradle工程,只需要在它自身的build.gradle文件中添加依賴關系:
dependencies {
compile project(':libraries:lib1')
}
android {
...
}
點擊這里查看更多多工程設置細節(jié)。
3.3 庫工程(Library projects)
上面所提到的多工程設置,:libraries:lib1, :libraries:lib2可以是Java工程,:app使用它們產(chǎn)生的jar包。但是,如果你想共享那些使用Android APIs或者使用Android-style的資源文件的代碼,就不能使用上述的普通的Java工程,必須是Android庫工程。
3.3.1 創(chuàng)建庫工程(Creating a Library Project)
庫工程和平常的Android工程很類似,有一些細小的區(qū)別。構(gòu)建庫工程(Library)和構(gòu)建一個應用工程(Application)是不同的,所以需要引用另一個插件'com.android.library',和com.android.application插件一樣,都是由com.android.tools.build.gradlejar包提供。下面是庫工程build.gradle文件的示例。
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
}
這個庫工程使用sdk編譯版本是23,SourceSet、buildTypes和dependencies都沿用他們所在的主工程,當然,也可以在庫工程自定義這些構(gòu)建信息。
3.3.2 庫工程和應用工程(主工程)的區(qū)別(Differences between a Project and a Library Project)
庫工程主要產(chǎn)出一個代表Android庫的aar包,里面包括編譯后的代碼(jar包和.so文件)和一些資源文件(manifest、res、assets);庫工程也可以構(gòu)建出一個測試apk用于測試庫工程,這個測試apk是獨立于主工程apk的,庫工程有assembleDebug和assembleRelease殼任務,所以用指令構(gòu)建庫工程和構(gòu)建主工程是沒有區(qū)別的。其余地方,庫功臣和主工程是相同的。他們都有構(gòu)建類型buildTypes和定制版本product flavors(后續(xù)會講解),可以產(chǎn)出多個版本的aar包。注意buildTypes中大多數(shù)構(gòu)建參數(shù)不適用于庫工程,同時,可以通過更改sourceSet更改庫工程的內(nèi)容,這取決于庫工程是被主工程使用,還是用于測試。
3.3.3 引用庫工程(Referencing a Library)
引用庫工程示例如下:
dependencies {
compile project(':libraries:lib1')
compile project(':libraries:lib2')
}
android {
...
}
3.3.4 發(fā)布庫工程(Library Publication)
庫工程會默認發(fā)布release版本,這個版本可以被其他所有的工程引用,與這些工程的構(gòu)建版本無關??梢酝ㄟ^設置參數(shù)控制庫工程發(fā)布的版本,示例如下:
android {
defaultPublishConfig "debug"
}
注意defaultPublishConfig內(nèi)容是構(gòu)建版本全名,release和debug是系統(tǒng)默認的構(gòu)建版本名,我們也可以改成我們自定義的構(gòu)建版本全名,示例如下:
android {
defaultPublishConfig "flavor1Debug"
}
當然,也可以發(fā)布所有版本的庫工程,示例如下:
android {
publishNonDefault true
}
發(fā)布多個庫工程版本意味著會產(chǎn)生多個aar包,而不是一個aar包包含多個版本,每個aar包都是一個獨立的版本。
不同的構(gòu)建可以依賴同一個庫工程的不同版本,示例如下:
dependencies {
flavor1Compile project(path: ':lib1', configuration: 'flavor1Release')
flavor2Compile project(path: ':lib1', configuration: 'flavor2Release')
}
注意:發(fā)布版本的defaultPublishConfig變量的內(nèi)容必須是構(gòu)建版本的完整名
注意:一旦設置了publishNonDefault true,會將所有版本的aar包都上傳到統(tǒng)一maven倉庫,但是,這種做法是不合理的,一個maven倉庫目錄應該僅對應一個系列版本的aar包,例如debug和release版本的aar包分別在不同的maven倉庫目錄中,或者保證不同版本的依賴僅僅發(fā)生在工程內(nèi)部,不上傳到maven倉庫。
4. 測試(Testing)
可以建立一個測試工程集成到主工程當中,不需要單獨新建一個測試工程。
4.1 單元測試(Unit testing)
Gradle 1.1版本之后就支持單元測試,點擊這里查看詳情。本章所提及的真機測試instrumentation tests,是指需要單獨構(gòu)建一個測試apk,運行在真機或者模擬器上的一種測試。
4.2 基本配置(Basics and Configuration)
上文中提到Android工程中默認有兩個目錄src/main/、src/androidTest/。使用src/androidTest/這個目錄中的資源會構(gòu)建一個使用Android測試框架,并且布署到真機(或測試機)上的測試apk來測試應用程序。Android測試框架包含單元測試、真機測試、UI自動化測試。測試apk的清單配置中的<instrumentation>節(jié)點會自動生成,同時用戶也可以在src/androidTest/AndroidManifest.xml中添加額外模塊用于測試。
下面列出測試apk中可能用到的屬性,點擊這里查看詳情:
- testApplicationId
- testInstrumentationRunner
- testHandleProfiling
- testFunctionalTest
這些屬性是在andorid.defaultConfig中配置的,示例如下:
android {
defaultConfig {
testApplicationId "com.test.foo"
testInstrumentationRunner "android.test.InstrumentationTestRunner"
testHandleProfiling true
testFunctionalTest true
}
}
在測試程序的清單配置(manifest)中,<instrumentation>節(jié)點中的targetPackage屬性會根據(jù)被測試的應用程式包名自動生成,這個屬性不受自定義的defaultConfig配置和buildType配置所影響。這也是manifest文件需要自動生成的一個原因。
另外,androidTest可以有自己的依賴配置,默認情況下,應用程序和它的依賴都會自動添加到測試應用的classpath中,也可以通過手動拓展測試的依賴,示例如下:
dependencies {
androidTestCompile 'com.google.guava:guava:11.0.2'
}
使用assembleAndroidTest任務來構(gòu)建測試apk,這個任務不依賴于主工程的assemble任務,當設置要執(zhí)行測試時候,這個任務會自動執(zhí)行。
默認只有一個buildType會被測試,debug的構(gòu)建類型,但是可以自定義修改被測試的buildType,示例如下:
android {
...
testBuildType "staging"
}
4.3 解決沖突(Resolving conflicts between main and test APK)
當啟動真機測試的時候,主apk和測試apk會共享同一個classpath,一旦兩個apk使用了同一個庫,但是使用的是不同版本,gralde構(gòu)建就會失敗。如果Gradle沒有捕獲這種情況,應用程式在測試和實際使用中可能表現(xiàn)不同(崩潰只是其中一種表現(xiàn))。
為了促使構(gòu)建成功,只需要讓所有的apk使用同一個版本的庫。如果這個沖突是發(fā)生在簡介依賴中(沒有直接在build.gradle中引入的庫),只需要在build.gradle中引入這個庫最新的版本即可,使用compile或者androidTestCompile。詳情查看這里Gradle's resolution strategy mechanism。可以通過./gradlew :app:dependencies and ./gradlew :app:androidDependencies查看工程的依賴樹。
4.4 執(zhí)行測試(Running tests)
上文中提到check類的任務(需要連接設備)是通過connectedCheck殼任務被喚起的,這個過程依賴connectedDebugAndroidTest任務,因此執(zhí)行測試,需要執(zhí)行connectedDebugAndroidTest任務,它會有以下操作:
- 確認主應用和測試應用都被構(gòu)建(依賴
assembleDebug和assembleDebugAndroidTest任務) - 安裝主應用和測試應用
- 運行測試
- 卸載主應用和測試應用
如果有多個設備連接,所有的測試會并行在所有設備上運行,其中任何一個設備測試失敗,測試就失敗。
4.5 測試Andorid庫(Testing Android Libraries)
測試Andriod庫和測試Android程序是相同的。不同的是Android庫作為依賴直接繼承到測試應用中,這樣測試apk不僅包含測試的代碼還包含這個庫以及這個庫的依賴。Android庫的清單配置會合并到測試程序的清單配置中。androidTest任務改為只安裝測試應用(沒有其他應用),其他都是相同的。
4.6 測試報告(Test reports)
當執(zhí)行單元測試后,Gradle會生成一份HTML報告方便查看測試結(jié)果。Andorid插件是在此基礎上擴展了HTML報告,聚合了所有連接設備的測試結(jié)果。所有的測試結(jié)果以XML形式儲存在build/reports/androidTests/目錄下,這個目錄也是可配的,示例如下:
android {
...
testOptions {
resultsDir = "${project.buildDir}/foo/results"
}
}
4.6.1 多工程測試報告(Multi-projects reports)
在配置了多工程或者多依賴的工程中,當同時運行所有測試時候,針對所有的測試只生成一份測試報告是非常有用的。
為了到達這個目的,需要使用另一個插件,這個插件是Android插件中自帶的,示例如下:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.5.6'
}
}
apply plugin: 'android-reporting'
這必須添加到工程的根目錄下,例如和settings.gradle同目錄的build.gralde中,然后在根目錄中使用使用一下指令運行所有測試,同時合并所有測試報告:
gradle deviceCheck mergeAndroidReports --continue
注意:--continue是為了保證所有測試都執(zhí)行,即使其中子項目中任何一個測試失敗。如果沒有這個選項,當有測試失敗時候,整個測試過程就會中斷。
4.7 Lint支持(Lint support)
注:lint是一種檢查Android項目的工具
可以針對某一個構(gòu)建版本執(zhí)行l(wèi)int,例如, ./gradlew lintRelease,或者針對所有版本./gradlew lint,lint會生成一個記錄被檢查版本問題的報告??梢酝ㄟ^配置lintOption來設置lint細節(jié),點擊這里查看詳情,示例如下:
android {
lintOptions {
// turn off checking the given issue id's
disable 'TypographyFractions','TypographyQuotes'
// turn on the given issue id's
enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
// check *only* the given issue id's
check 'NewApi', 'InlinedApi'
}
}
5. 構(gòu)建版本(Build Variants)
使用新構(gòu)建工具的目的之一是面對同一個工程,能夠編譯出不同的版本。
有兩個場景會用到:
- 同一個應用有多個版本,例如,免費版本和付費版本
- 同一個應用針對不同的設備有多個版本,比如說手機版和pad版,點擊這里查看詳情
- 1和2綜合
針對同一個工程,可以編譯出不同版本的apks,而不是為了編譯出不同版本的apks,使用多個工程。
5.1 Product flavors
product flavor可自定義應用的版本,同一個工程中可以有多個不同的product flavor。
product flavor用來告訴構(gòu)建系統(tǒng)不同版本之間的細小區(qū)別,product flavor的聲明示例如下:
android {
....
productFlavors {
flavor1 {
...
}
flavor2 {
...
}
}
}
里面創(chuàng)建了兩個product flavor,分別是flavor1和flavor2。
注意:product flavor的名稱不能和構(gòu)建類型(buildType)的名稱相同,也不能是androidTest和test。
5.2 Build Type + Product Flavor = Build Variant
構(gòu)建類型 + 定制版本 = 構(gòu)建版本(應用版本)
就像上文提到的,每一個構(gòu)建類型(buildType)都可以構(gòu)建一個apk,同樣的,每一個定制版本(productFlavor)都可以構(gòu)建一個apk,這樣的話,構(gòu)建類型(buildType)和定制版本(productFlavor)結(jié)合就會形成一個新的apk,也就是構(gòu)建版本。默認有兩種構(gòu)建類型debug和release,再加上上文定義的flavor1和flavor2,就會形成四種組合,代表四種不同的構(gòu)建版本:
- Flavor1 - debug
- Flavor1 - release
- Flavor2 - debug
- Flavor2 - release
一個有沒有定義productFlavors的工程也有構(gòu)建版本,使用缺省的productFlavors,也就是沒有product flavor名稱,那么工程所有的構(gòu)建版本和構(gòu)建類型是一樣的。
5.3 配置定制版本(Product Flavor Configuration)
productFlavors配置示例如下:
android {
...
defaultConfig {
minSdkVersion 8
versionCode 10
}
productFlavors {
flavor1 {
applicationId "com.example.flavor1"
versionCode 20
}
flavor2 {
applicationId "com.example.flavor2"
minSdkVersion 14
}
}
}
注意,android.productFlavors.*和android.defaultConfig中可配置的參數(shù)是相同的,也就是說他們共享這些參數(shù)。
defaultConfig為所有productFlavor提供一些基本配置參數(shù),每一個productFlavors自定義一些額外的配置參數(shù),也可以覆蓋defaultConfig中配置的參數(shù)。在上面的示例中,productFlavors配置信息如下:
- flavor1
- applicationId: com.example.flavor1
- minSdkVersion: 8
- versionCode: 20
- flavor2
- applicationId: com.example.flavor2
- minSdkVersion: 14
- versionCode: 10
通常,構(gòu)建類型(buildType)也會修改一些配置信息,比如說buildType中的applicationIdSuffix變量拼接在productFlavor的applicationId之后的。但是有些配置參數(shù)是以productFlavor為主的,比如signingConfig,所有的release版本的構(gòu)建都會使用android.buildTypes.release.signingConfig中配置的簽名信息,如果設置了productFlavor,所有的release版本的構(gòu)建都會使用android.productFlavors.*.signingConfig中配置的簽名信息。
5.4 資源目錄和依賴(Sourcesets and Dependencies)
和構(gòu)建類型相似,productFlavor也有自己的資源目錄,示例如下:
- android.sourceSets.flavor1,資源目錄是
src/flavor1/ - android.sourceSets.flavor2,資源目錄是
src/flavor2/ - android.sourceSets.androidTestFlavor1,資源目錄是
src/androidTestFlavor1/ - android.sourceSets.androidTestFlavor2,資源目錄是
src/androidTestFlavor2/
這些資源目錄中的資源會用于構(gòu)建apk,構(gòu)建apk資源的來源是android.sourceSets.main主工程的資源和構(gòu)建類型的資源目錄(或者productFlavor的資源目錄)。下面是多個資源目錄構(gòu)建規(guī)則:
- 所有的資源代碼(src/*/java)最終都會合并到最后輸出包中
- 所有的Manifests.xml也會合并,根據(jù)
buildTypes和productFlavor,每個不同的構(gòu)建包apk,會有不同的組件和權(quán)限 - 所有的資源(包括res和assets)都會做合并,資源會做合并,資源優(yōu)先級
buildType>productFlavor> 主工程 - 每一個構(gòu)建版本都有唯一的一個R.class,不和其他構(gòu)建版本共享
最后,和構(gòu)建類型(buildType)一樣,每一個productFlavor都有自己的依賴。例如flavor1需要依賴廣告組件和支付組件,而flavor2僅僅依賴廣告組件,配置文件如下:
dependencies {
flavor1Compile "ads.sdk"
flavor1Compile "pay.sdk"
flavor2Compile "ads.sdk"
}
另外每一個構(gòu)建版本都有一個資源目錄,示例如下:
- android.sourceSets.flavor1Debug,資源目錄
src/flavor1Debug/ - android.sourceSets.flavor1Release,資源目錄
src/flavor1Release/ - android.sourceSets.flavor2Debug,資源目錄
src/flavor2Debug/ - android.sourceSets.flavor2Release,資源目錄
src/flavor2Release/
構(gòu)建版本目錄資源的優(yōu)先級高于構(gòu)建類型資源目錄。
現(xiàn)在基本知道,buildTypes、productFlavor、buildVariants都有自己的資源目錄,資源優(yōu)先級是:
buildVariants>buildType>productFlavor> 主工程。
5.5 構(gòu)建任務(Building and Tasks)
上文中提到,每新建一種構(gòu)建類型buildType,都會自動創(chuàng)建一個名為assemble<Build Type Name>的新任務。
而每新建一種productFlavor,會自動創(chuàng)建多個新任務:
-
assemble<Variant Name>,直接構(gòu)建一個最終版本的apk,例如assembleFlavor1Debug任務 -
assemble<Build Type Name>,構(gòu)建所有buildType類型的apks,例如,assembleDebug任務會構(gòu)建出Flavor1Debug和Flavor2Debug版本的apk -
assemble<Product Flavor Name>,構(gòu)建所有productFlavor的apks,例如,assembleFlavor1任務會構(gòu)建出Flavor1Debug和Flavor1Release版本的apk.
assemble會構(gòu)建所有版本的apk。
5.6 多flavor構(gòu)建(Multi-flavor variants)
注:原文中dimension of Product Flavors,統(tǒng)一翻譯為productFlavor類型,在某些文檔中也翻譯成維度
在某些場景下,一個應用可能需要基于多個標準創(chuàng)建多個版本。例如,Google Play的multi-apk支持四個不同的過濾器,這些用于創(chuàng)建不同apk的過濾器需要使用多個類型的ProductFlavor。
例如,一個游戲有免費版本和付費版本,同時在multi-apk中需要支持ABI過濾器(ABI,二進制接口,可以讓編譯好的目標代碼在所有支持該ABI的系統(tǒng)上運行,而無需對程序進行修改)。一個擁有兩個版本和三個ABI過濾器的工程,需要創(chuàng)建六個apks(不考慮構(gòu)建類型buildType),但是它們使用的源代碼都是相同的,所以沒有必要創(chuàng)建六個productFlavor。相反,只需要創(chuàng)建兩個類型的flavor,就可以構(gòu)建出所有的可能的版本組合。
使用flavorDimensions數(shù)組來實現(xiàn)多個類型的flavor,每一個productFlavor被分到不同的類型,示例如下:
android {
...
flavorDimensions "abi", "version"
productFlavors {
freeapp {
dimension "version"
...
}
paidapp {
dimension "version"
...
}
arm {
dimension "abi"
...
}
mips {
dimension "abi"
...
}
x86 {
dimension "abi"
...
}
}
}
android.flavorDimensions數(shù)組按順序定義了可能使用到的flavor類型,每一個productFlavor聲明自身的flavor類型。
上面例子中,將productFlavor分為兩個類型,abi類型[arm, mips, x86]和version類型[freeapp, paidapp],加上默認的[debug, release]構(gòu)建類型,將會組合出以下這些構(gòu)建版本(Build Variant):
- x86-freeapp-debug
- x86-freeapp-release
- arm-freeapp-debug
- arm-freeapp-release
- mips-freeapp-debug
- mips-freeapp-release
- x86-paidapp-debug
- x86-paidapp-release
- arm-paidapp-debug
- arm-paidapp-release
- mips-paidapp-debug
- mips-paidapp-release
android.flavorDimensions數(shù)組定義的flavor類型順序非常重要。
上述每一個構(gòu)建版本名稱都由以下幾個屬性構(gòu)成:
- android.defaultConfig
- abi類型
- version類型
多flavor工程也有自身的資源目錄,和構(gòu)建版本目錄相似但是目錄名稱不包含構(gòu)建類型,例如:
- android.sourceSets.x86Freeapp,資源目錄是
src/x86Freeapp/ - android.sourceSets.armPaidapp,資源目錄是
src/armPaidapp/
多flavor的資源目錄優(yōu)先級高于productFlavor資源目錄,但是低于構(gòu)建類型資源目錄優(yōu)先級。
那么就可以列出整個工程資源優(yōu)先級,資源優(yōu)先級是:
buildVariants>buildType> 多flavor>productFlavor> 主工程
5.7 測試(Testing)
測試多flavor項目和測試一般項目類似。
androidTest的目錄適用于所有flavor的測試,每一個flavor也有單獨的測試資源目錄,例如:
- android.sourceSets.androidTestFlavor1,資源目錄
src/androidTestFlavor1/ - android.sourceSets.androidTestFlavor2,資源目錄
src/androidTestFlavor2/
類似的,每個flavor也有自己的依賴配置,示例如下:
dependencies {
androidTestFlavor1Compile "..."
}
通過deviceCheck任務或者主工程的androidTest任務會執(zhí)行androidTestFlavor1Compile任務。
每個flavor也有自己任務用于執(zhí)行測試,androidTest<VariantName>,例如:
- androidTestFlavor1Debug
- androidTestFlavor2Debug
類似的,測試apk的構(gòu)建、安裝、卸載任務:
- assembleFlavor1Test
- installFlavor1Debug
- installFlavor1Test
- uninstallFlavor1Debug
- ...
最終,會根據(jù)flavor生成HTML測試報告,也會生成集成測試報告。測試報告的目錄示例如下:
- build/androidTest-results/flavors/<FlavorName>,單個
flavor測試報告的目錄 - build/androidTest-results/all/,合并
flavor的測試報告 - build/reports/androidTests/flavors<FlavorName>,單個
flavor測試報告的 - build/reports/androidTests/all/,合并
flavor的測試報告
即使自定義目錄,也只會改變根目錄,里面的具體子目錄不會改變。
5.8 構(gòu)建配置(BuildConfig)
在編譯時,Android Studio會生成一個類BuildConfig,這個類包含構(gòu)建特定版本時用到的一些常量,用戶可以根據(jù)這些常量執(zhí)行不同的操作行為。例如:
private void javaCode() {
if (BuildConfig.FLAVOR.equals("paidapp")) {
doIt();
else {
showOnlyInPaidAppDialog();
}
}
下面是BuildConfig類包含的一些常量:
- boolean DEBUG – if the build is debuggable.
- int VERSION_CODE
- String VERSION_NAME
- String APPLICATION_ID
- String BUILD_TYPE – 構(gòu)建類型,例如: "release"
- String FLAVOR –
productFlavor名稱,例如: "paidapp"
如果工程中使用了flavorDimensions多類型flavor,會自動生成額外的變量。以上述的配置文件為例:
- String FLAVOR = "armFreeapp"
- String FLAVOR_abi = "arm"
- String FLAVOR_version = "freeapp"
5.9 過濾構(gòu)建版本(Filtering Variants)
當添加productFlavor或者使用flavorDimensions設置多類型flavor,可能有些構(gòu)建版本并不需要。例如,用戶定義了兩個productFlavor,一個是正常版本,另一個仿造數(shù)據(jù)用于測試。第二個productFlavor僅僅是在開發(fā)過程中有用,在構(gòu)建發(fā)布包時不需要這個productFlavor,可以通過使用variantFilter過濾器移除不需要的構(gòu)建版本。示例如下:
android {
productFlavors {
realData
fakeData
}
variantFilter { variant ->
def names = variant.flavors*.name
if (names.contains("fakeData") && variant.buildType.name == "release") {
variant.ignore = true
}
}
}
使用以上配置后,工程就只有以下的構(gòu)建版本:
- realDataDebug
- realDataRelease
- fakeDataDebug
點擊這里查看可以過濾的構(gòu)建屬性。
6. 構(gòu)建定制進階(Advanced Build Customization)
6.1 混淆(Running ProGuard)
ProGuard插件是Android插件中自帶的,如果構(gòu)建任務(Build Type)中通過設置minifyEnabled為true(意為使用混淆),混淆任務會自動創(chuàng)建。示例如下:
android {
buildTypes {
release {
minifyEnabled true
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
productFlavors {
flavor1 {
}
flavor2 {
proguardFile 'some-other-rules.txt'
}
}
}
構(gòu)建版本同時使用構(gòu)建類型(Build Type)和productFlavor中設置的混淆規(guī)則文件。
默認有兩個混淆規(guī)則文件:
- proguard-android.txt
- proguard-android-optimize.txt
它們位于Android SDK中,使用getDefaultProguardFile(fileName)獲取它們的絕對路徑名,差別是第二個文件會啟用優(yōu)化。
6.2 忽略資源(Shrinking Resources)
這個配置設置為true,在構(gòu)建打包時候,會自動忽略沒有被使用到的文件。點擊這里查看詳細信息。示例如下:
android {
buildTypes {
release {
shrinkResources true
...
}
}
}
6.3 操作任務(Manipulating tasks)
Java工程使用固定的任務一起協(xié)作最終打包工程。其中classes任務是用來編譯Java源代碼的,可以在build.gradle使用classes,它是project.tasks.classes的縮寫。
在Android工程中,如果要構(gòu)建打包,可能會復雜一些,因為Android工程中有大量名字相同的任務,而且它們的名字是基于buildType和productFlavor生成的。
為了解決這個問題,Android對象有兩個屬性:
- applicationVariants,只適用于
com.android.application插件 - libraryVariants,只適用于
com.android.library插件 - testVariants,兩種插件都適用
這三個屬性分別返回一個ApplicationVariant、LibraryVariant和TestVariant對象的DomainObjectCollection。
注意,適用這三個collection中的任意一個,都會生成所有相對應的任務,也就是說使用collection后,就不需要再更改配置。
DomainObjectCollection可以直接訪問所有對象,或者通過過濾器進行篩選。
android.applicationVariants.all { variant ->
....
}
這三個Variant類共享下面這些屬性:
| 屬性名 | 屬性類型 | 描述 |
|---|---|---|
| name | String | BuildVariant名稱,必須保證唯一 |
| description | String | BuildVariant的描述說明 |
| dirName | String | BuildVariant的子文件名,必須是唯一的可能會有多個,例如:debug/flavor1 |
| baseName | String | BuildVariant構(gòu)建包的基礎名字,必須唯一 |
| outputFile | File | BuildVariant的輸出文件,是個可讀寫的屬性 |
| processManifest | ProcessManifest | 處理清單Manifest的任務 |
| aidlCompile | AidlCompile | 編譯AIDL文件的任務 |
| renderscriptCompile | RenderscriptCompile | 處理Renderscript文件的任務 |
| mergeResources | MergeResources | 合并資源的任務 |
| mergeAssets | MergeAssets | 合并assets資源的任務 |
| processResources | ProcessAndroidResources | 處理和編譯資源文件的任務 |
| generateBuildConfig | GenerateBuildConfig | 生成BuildConfig的任務 |
| javaCompile | JavaCompile | 編譯Java源代碼的任務 |
| processJavaResources | Copy | 處理Java資源的任務 |
| assemble | DefaultTask | BuildVariant構(gòu)建殼任務 |
ApplicationVariant類還有以下附加屬性:
| 屬性名 | 屬性類型 | 描述 |
|---|---|---|
| buildType | BuildType | BuildVariant的構(gòu)建類型 |
| productFlavors | List<ProductFlavor> | BuildVariant的productFlavor,不會為null但可以為空 |
| mergedFlavor | ProductFlavor | 合并android.defaultConfig和variant.productFlavors的任務 |
| signingConfig | SigningConfig | BuildVariant使用的簽名 |
| isSigningReady | boolean | true表示BuildVariant配置了簽名所需要的信息 |
| testVariant | BuildVariant | 用于測試這個BuildVariant的BuildVariant |
| dex | Dex | 將代碼打包成dex的任務,如果是庫工程,那么這個任務不能為null |
| packageApplication | PackageApplication | 打包最終apk的任務,如果是個庫工程,這個任務可以為null |
| zipAlign | ZipAlign | zip壓縮apk的任務,如果是個庫工程或者apk不被簽名,這個任務可以為null |
| install | DefaultTask | 安裝apk任務,不能為null |
| uninstall | DefaultTask | 卸載apk任務 |
LibraryVariant類有以下附加屬性:
| 屬性名 | 屬性類型 | 描述 |
|---|---|---|
| buildType | BuildType | BuildVariant的構(gòu)建類型 |
| mergedFlavor | ProductFlavor | The defaultConfig values |
| testVariant | BuildVariant | 用于測試這個BuildVariant的BuildVariant |
| packageLibrary | Zip | 用于打包aar的任務,如果是庫工程,不能為null |
TestVariant類有以下附加屬性:
| 屬性名 | 屬性類型 | 描述 |
|---|---|---|
| buildType | BuildType | BuildVariant的構(gòu)建類型 |
| productFlavors | List<ProductFlavor> | BuildVariant的productFlavor,不會為null但可以為空 |
| mergedFlavor | ProductFlavor | 合并android.defaultConfig和variant.productFlavors的任務 |
| signingConfig | SigningConfig | BuildVariant使用的簽名 |
| isSigningReady | boolean | true表示BuildVariant配置了簽名所需要的信息 |
| testedVariant | BaseVariant | 被TestVariant測試的BaseVariant |
| dex | Dex | 將代碼打包成dex的任務,如果是庫工程,那么這個任務不能為null |
| packageApplication | PackageApplication | 打包最終apk的任務,如果是個庫工程,這個任務可以為null |
| zipAlign | ZipAlign | zip壓縮apk的任務,如果是個庫工程或者apk不被簽名,這個任務可以為null |
| install | DefaultTask | 安裝apk任務,不能為null |
| uninstall | DefaultTask | 卸載apk任務 |
| connectedAndroidTest | DefaultTask | 在連接設備上執(zhí)行Android測試的任務 |
| providerAndroidTest | DefaultTask | 使用拓展API執(zhí)行Android測試的任務 |
Android特有任務類型的API:
- ProcessManifest
- File manifestOutputFile
- AidlCompile
- File sourceOutputDir
- RenderscriptCompile
- File sourceOutputDir
- File resOutputDir
- MergeResources
- File outputDir
- MergeAssets
- File outputDir
- ProcessAndroidResources
- File manifestFile
- File resDir
- File assetsDir
- File sourceOutputDir
- File textSymbolOutputDir
- File packageOutputFile
- File proguardOutputFile
- GenerateBuildConfig
- File sourceOutputDir
- Dex
- File outputFolder
- PackageApplication
- File resourceFile
- File dexFile
- File javaResourceDir
- File jniDir
- File outputFile
- 在Variant對象中修改
outputFile屬性可以改變最終輸出的文件夾.
- 在Variant對象中修改
- ZipAlign
- File inputFile
- File outputFile
- 在Variant對象中修改
outputFile屬性可以改變最終輸出的文件夾.
- 在Variant對象中修改
每一個任務類型的API由于Gradle的工作方式以及Android插件配置方式而受到限制。首先,Gradle任務只能被配置輸入和輸出的目錄以及一些可能使用到的常量,其次大多數(shù)任務的輸出都不是固定單一的,一般都混合了sourceSet、Build Type和Product Flavor中的值。這都是為了保證構(gòu)建文件的簡單和可讀性,讓開發(fā)者通過DSL語言去修改構(gòu)建過程,而不是深入修改任務并改變構(gòu)建過程。
同時,除了ZipAlign任務,其他類型的任務都需要設置私有數(shù)據(jù)讓它們運行,這就意味著無法手動創(chuàng)建這些類型的新任務。
這些API也有可能被更改,目前大部分API都是圍繞著給定任務的輸入輸出來添加外的處理。
6.4 配置JDK版本(Setting language level)
默認會根據(jù)compileSdkVersion來選擇JDK版本,可以通過compileOptions設置編譯時使用的JDK版本。示例如下:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
}
}