Android Gradle外掛程式使用者指南(譯),androidgradle
Android Gradle外掛程式使用者指南(譯)
原文Gradle Plugin User Guide - Android Tools Project Site
samples see bottom of New Build System
參考Gradle For Android Training Course
1 簡介
這篇文檔是基於0.9版本的Gradle外掛程式,1.0以前的版本由於不相容,可能會有所不同
1.1 新的構建系統的目標
新構建系統的目標是:
- 使得代碼和資源的重用更加簡單
- 使得建立同一應用程式的不同版本更加容易,不管是多個apk版本還是同一版本的多種定製
- 使得配置,擴充和自訂構建更加容易
- 良好的IDE整合
1.2 為什麼使用Gradle
Gradle是一個進階構建系統和構建工具,允許通過外掛程式自訂構建邏輯
以下一些功能使得我們選擇Gradle:
- 使用特定領域語言(DSL)來描述和控制構建邏輯
- 構建指令碼基於Groovy語言,允許通過DSL混合元素宣告和通過代碼控制DSL元素,來產生自訂的構建邏輯
- 支援Maven和(或者)Ivy管理依賴
- 非常靈活。允許使用最佳實務,但也不強制自己的實現方式
- 外掛程式能夠提供自己的DSL和API供構建指令碼使用
- 提供優秀的工具API以供IDE整合
2 環境要求
- Gradle 1.10或者1.11或者1.12,以及外掛程式版本0.11.1
- SDK以及Buid Tools 19.0.0,某些功能可能需要更新的版本
3 基本項目
Gradle項目通過項目根目錄下的 build.gradle 檔案來描述構建過程
3.1 簡單的構建檔案
最簡單的Java項目構建檔案 build.gradle
apply plugin: 'java'
這個指令碼應用了Gradle的Java外掛程式。這個外掛程式了提供構建和測試Java應用的所有功能
最簡單的Android項目的構建檔案包含以下內容:
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.11.1' }}apply plugin: 'android'android { compileSdkVersion 19 buildToolsVersion "19.0.0"}
Note(譯註): 最新的android外掛程式聲明
apply plugin: 'com.android.application'
在這個Android構建指令碼裡包含了三個主要內容:
buildscript { ... } 配置了驅動構建過程的代碼。在這個案例中,聲明了使用Maven倉庫,以及一個Maven檔案(artifact)的依賴路徑。這個檔案就是包含了Android Gradle外掛程式的庫,版本為0.11.1
然後,android外掛程式被應用,像之前的Java外掛程式一樣
最後,android { ... } 配置了anroid構建過程需要的參數。這也是Adnroid DSL的入口。預設的情況下,只有編譯目標SDK版本,和構建工具版本是必須的。在指令碼中,對應的是compileSdkVersion和buildtoolsVersion屬性。compileSdkVersion和舊編譯系統中project.properties檔案中的target屬性對應。這個新屬性compileSdkVersion可以是一個int值(API Level)或者一個和之前的target屬性值一樣的字串
重點: 你應該只應用android外掛程式,同時應用java外掛程式會導致構建錯誤
注意: 你同樣需要一個local.properties檔案來指明SDK的路徑,和build.gradle在同一路徑下,在檔案中使用sdk.dir屬性指明。或者,你可以設定ANDROID_HOME環境變數。兩者是一致的,你可以選擇一種你喜歡的方式。
3.2 項目結構
前面的android構建指令碼使用了預設的檔案夾目錄結構。Gradle遵循約定優於配置的原則,在可能的情況下提供了合理的預設配置參數。
基本的項目包含兩個“source sets”組件。main source code和test code,位於以下的目錄中:
src/main/src/androidTest/
在這些目錄中,都存在目錄對應源碼組件
不管是Java還是Android外掛程式,源碼目錄和資來源目錄都如下 :
java/resources/
對於Android外掛程式,還有特有的檔案和目錄
AndroidManifest.xmlres/assets/aidl/rs/jni/
Note: src/androidTest/AndroidManifest.xml 不是必須的,會自動被建立。
3.2.1 設定項目結構
當預設項目結構不合適的時候,可以設定項目目錄。根據Gradle文檔,可以通過下面的指令碼重新設定Java項目的sourceSets:
sourceSets { main { java { srcDir 'src/java' } resources { srcDir 'src/resources' } }}
Note: srcDir 會添加指定的目錄到源檔案目錄列表中(這在Gradele文檔中沒有提及,但是實際上是這樣的)。
為了替換預設的源檔案目錄列表,可以使用srcDirs來指定目錄數組。這也是一種不同的使用方式:
sourceSets { main.java.srcDirs = ['src/java'] main.resources.srcDirs = ['src/resources']}
更多的資訊,參考Gradle文檔中的Java外掛程式內容
Android外掛程式使用相似的文法,但是由於使用是自己的sourceSets,相應的目錄在(build.gradle檔案中的)android對象中指定
下面是一個樣本,它使用舊項目的源碼結構,並且將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') }}
Note: 由於舊的結構將所有的源檔案 (java, aidl, renderscript, and java資源檔)放在一個目錄裡,我們需要映射這些sourceSet組件到src目錄。
Note: setRoot() 方法將整個sourceSet(包含子目錄)指向新的目錄。比如上面,將src/androidTest/* 指向了 tests/*
以上是Android特有的,如果配置在Java sourceSets中就沒有作用
‘migrated’ 樣本(位於本頁面底部)中展示了這部分內容
3.3 構建任務3.3.1 通用任務
在build檔案中應用一個外掛程式將自動建立一系列構建任務。Java外掛程式和Android外掛程式都是這樣。任務約定如下:
組合項目輸出
執行所有檢查
執行assemble和check兩個task的所有工作
清理項目輸出
任務assemble, check 和 build 不會做任何實際的事情。他們只是錨點任務(anchor tasks),外掛程式依賴他們來添加實際執行實際操作的任務。
這樣就不需要考慮項目是什麼類型,使用的是什麼外掛程式,都可以執行同樣的任務。
例如,使用findbugs外掛程式,會建立新的任務,並讓check依賴這個任務,使得check被調用時這個任務就會被調用。
在終端(命令列,gradle項目目錄下)中運行下面的任務可以查詢到進階別的任務:
gradle tasks
運行以下命令可以看到全部任務和任務依賴關係:
gradle tasks --all
Note: Gradle自動監視一個任務聲明的輸入輸出檔案。再次執行構建任務時,如果檔案沒有改變,Gradle會指明所有任務為UP-TO-DATE,意味著任務不需要執行。這樣的話,任務可以正確地互相依賴,而不不會導致非必須的構建操作
3.3.2 Java項目的任務
Java外掛程式主要建立兩個任務,下面是這兩個錨點任務的依賴關係
jar任務直接或間接地依賴其他任務:例如classes任務將編譯Java源碼
testClasses任務用於編譯測試的,但是這個任務很少被調用,因為test任務依賴於它(就像依賴classes任務一樣)
通常來說,你只需要調用assemble或者check任務,而不需要調用其他任務。
你可以在Gradle Java外掛程式文檔看到Java外掛程式的全部任務和它們的描述
3.3.3 Android任務
Android外掛程式使用同樣的約定來保持和其他外掛程式的相容,並且添加了額外的錨點任務:
新的錨點任務是必須的,以保證在不需要裝置串連的情況下能運行常規檢查。
需要注意的是,build任務並不依賴deviceCheck或者connectedCheck
一個Android項目至少有兩個輸出:debug APK 和 release APK。它們每一個都有自己的錨點任務來協助它們完成獨立的構建:
- assemble
- assembleDebug
- assembleRelease
它們都依賴其它任務來完成構建一個apk所需要的多個步驟。assemble任務依賴這兩個任務,所以調用assemble會產生兩個APK。
Tip: Gradle支援在命令列中使用camel形式的任務名縮寫。
例如:
gradle aR和gradle assembleRelease是一樣的,因為沒有別的任務名有同樣的縮寫
錨點任務check也有自己的依賴:
- check
- connectedCheck
- connectedAndroidTest
- connectedUiAutomatorTest (not implemented yet)
- deviceCheck
- 依賴於當其它外掛程式實現測試擴充點時所建立的任務。
最終,外掛程式會為所有構建類型(debug, release, test)建立install/uninstall任務,如果輸出檔案可以安裝的話(必須簽名)。
3.4 基本的構建過程定製
Android外掛程式提供了大量DSL來直接從構建系統中定製大多數事情。
3.4.1 Manifest屬性
通過DSL,可以配置以下manifest屬性:
- minSdkVersion
- targetSdkVersion
- versionCode
- versionName
- applicationId (實際的packageName – 前往 ApplicationId versus PackageName 查看更多)
- Package Name for the test application
- Instrumentation test runner
例如:
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.1版本開始,你應該在build.gradle檔案使用applicationId來配置manifest檔案的packageName屬性。
這是為了消除Android應用的packageName(作為Android應用的ID)和java包名之間的疑義。
在構建檔案中定義的強大之處在於它可以是動態。
例如,可以從一個檔案中讀取版本名稱,或者使用自訂的邏輯:
def computeVersionName() { ...}android { compileSdkVersion 19 buildToolsVersion "19.0.0" defaultConfig { versionCode 12 versionName computeVersionName() minSdkVersion 16 targetSdkVersion 16 }}
Note: 函數名不要與指定範圍內已經存在的getter方法名衝突。例如,在defaultConfig { ...}中調用getVersionName()會自動使用defaultConfig.getVersionName(),而不是你自訂的其它getVersionName()。
如果屬性沒有通過DSL設定,那麼預設的屬性值會被使用。下面是預設的屬性值列表:
| 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) |
如果你在構建指令碼中使用自訂的邏輯讀取這些屬性,那麼第二列的屬性就很重要。例如,你可能這樣寫:
if (android.defaultConfig.testInstrumentationRunner == null) { // assign a better default...}
如果這個值是null,那麼在構建過程中會被第三列的預設值替代,但是DSL元素不會包含這個預設值(第三列的值),所以你查詢不到這個值。這是為了防止解析應用的manifest檔案,除非真的必要。
3.4.2 構建類型(Build Types)
預設情況下,Android外掛程式會自動化佈建項目同時構建debug和release版本的應用程式。
這兩個版本的不同之處主要在於能否在一個安全裝置上偵錯工具,和APK如何簽名。
debug版本使用一個自動建立的密鑰/認證,並使用已知的name/password來簽名(防止構建過程中出現請求提示)。release版本在構建過程中沒有簽名,需要稍後簽名。
這些配置通過一個構建類型(BuildTpye)對象來設定。預設情況下,debug和release這兩個構建類型都會被建立。
Android外掛程式允許自訂這兩個執行個體,也允許建立其它構建類型。這些都在buildTypes的DSL容器中配置:
android { buildTypes { debug { applicationIdSuffix ".debug" } jnidebug.initWith(buildTypes.debug) jnidebug { packageNameSuffix ".jnidebug" jniDebuggable true } }}
上面的程式碼片段完成來以下功能:
建立一個新的構建類型就像在buildTypes容器中使用一個新的元素一樣簡單,可以通過調用initWith()或者使用閉包來配置
以下是可能用到的屬性和它們的預設值:
| Property name |
Default values for debug |
Default values for release / other |
| debuggable |
true |
false |
| jniDebuggable |
false |
false |
| renderscriptDebuggable |
false |
false |
| renderscriptOptimLevel |
3 |
3 |
| applicationIdSuffix |
null |
null |
| versionNameSuffix |
null |
null |
| signingConfig |
android.signingConfigs.debug |
null |
| zipAlignEnabled |
false |
true |
| minifyEnabled |
false |
false |
| proguardFile |
N/A (set only) |
N/A (set only) |
| proguardFiles |
N/A (set only) |
N/A (set only) |
除了以上這些屬性,Build Types還可以通過源碼和資源來影響構建過程。
每一個構建類型都會建立一個匹配的sourceSet,預設的路徑為:
src/<buildtypename>/
這意味這新的構建類型的名字不能是main或者androidTest(這是外掛程式強制要求的),而構建類型的名稱必須是唯一的。
像其它sourceSet一樣,構建類型的sourceSet可以重新被定向:
android { sourceSets.jnidebug.setRoot('foo/jnidebug')}
另外,每一個Build Type都會建立一個assemble<BuildTypeName>任務。
在前面,assembleDebug和assembleRelease已經提到過了,這就是它們的來源。當debug和release構建類型被預建立的時候,它們相關的任務就被自動建立了,比如assembleDebug和assembleRelease
上面的build.gradle片段同樣會建立assembleJnidebug任務,assemble會像依賴assembleDebug和assembleRelease任務一樣依賴assembleJnidebug。
Tip: 你可以在命令列下輸入gradle aJ來運行assembleJnidebug任務。
可能用到的情境:
- 只有debug模式才需要的許可權,而release模式不需要
- 自訂debug實現
- debug模式使用不同的資源(例如,資源取值與簽署憑證綁定)
BuildType的源碼和資源通過以下方式使用:
- manifest檔案合并到app的manifest檔案中
- 源碼作為另一個源碼目錄
- 資源疊加到main的資源中,取代已經存在的值
3.4.3 簽名配置
對一個應用程式簽名需要以下:
- 一個keystore
- 一個keystore密碼
- 一個key的別名
- 一個key密碼
- 儲存類型
位置,別名,兩個密碼和儲存類型一個組成一個簽名配置(SigningConfig)
預設情況下,debug簽名配置使用一個debug keystore,已知的密碼和已知的別名以及別名密碼。
debug keystore位於$HOME/.android/debug.keystore,如果沒有的話會自動建立一個。
debug構建類型會自動使用debug SigningConfig。
可以建立其它簽名配置或者自訂預設內建配置。通過signingConfigs DSL容器來配置
android { signingConfigs { debug { storeFile file("debug.keystore") } myConfig { storeFile file("other.keystore") storePassword "android" keyAlias "androiddebugkey" keyPassword "android" } } buildTypes { foo { debuggable true jniDebuggable true signingConfig signingConfigs.myConfig } }}
上面的片段修改debug keystore的位置到項目根目錄下。這會影響任何使用它的構建類型,在這個案例中,受影響的是debug構建類型。
這裡也建立了一個新的簽名配置和一個使用這個新簽名配置的行的構建類型。
Note: 只有預設路徑下debug keystore不存在的時候會被自動建立。改變debug keystore的路徑則不會在新的路徑下自動建立debug keystore。建立一個名字不同的簽名配置,但是使用預設的debug keystore路徑,會自動建立debug keystore。也就是說,是否自動建立debug keystore,是由keystore的位置決定,而不是配置的名稱。
Note: keystore的路徑通常是項目根目錄的相對路徑,但是也可以使用絕對路徑,儘管不推薦這樣(debug keystore除外,因為它會自動被建立)。
**Note: 如果你要把這些檔案添加到版本控制系統中,你可能不想把密碼寫在檔案中。下面的Stack Overflow串連提供了從從控制台或者環境變數讀取的方法。
http://stackoverflow.com/questions/18328730/how-to-create-a-release-signed-apk-file-using-gradle
我們以後會更新指南,提供更多的細節**
3.4.4 運行ProGuard
ProGuard從Gradle plugin for ProGuard 4.10開始支援的(since Gradle plugin 0.4)。如果構建類型的minifyEnabled屬性被設定為true,那麼Progruard外掛程式會自動被添加進來,對應的任務也自動被建立。
Note: 從Gradle外掛程式版本0.14.0開始BuildType.runProguard更改為minifyEnabled屬性。具體請參考Release notes
android { buildTypes { release { minifyEnabled true proguardFile getDefaultProguardFile('proguard-android.txt') } } productFlavors { flavor1 { } flavor2 { proguardFile 'some-other-rules.txt' } }}
Variant會使用所有聲明的規則檔案,包括聲明在相應的Build Type和flavor中的。
SDK中有兩個預設的規則檔案:
- proguard-android.txt
- proguard-android-optimize.txt
它們位於sdk路徑下,使用getDefaultProguardFile()可以擷取檔案的完整路徑。它們除了是否要進行最佳化之外,其它都是相同的。
3.4.5 壓縮資源檔
構建時可以自動移除沒有被使用的資源檔。更多詳細資料請查看文檔資源檔壓縮
4 依賴關係,Android庫項目和多項目設定
Gradle項目可以依賴其它組件,這些組件可以是外部二進位包,或者其它Gradle項目。
4.1 依賴二進位包4.1.1 當地套件
為了配置一個外部庫jar依賴,你需要在compile配置中添加一個依賴
dependencies { compile files('libs/foo.jar')}android { ...}
Note: dependenciesDSL元素是標準Gradle API的一部分,並不屬於android元素。
compile配置是用來編譯main應用的。任何添加到編譯路徑中的東西都會被打包到最終的apk檔案中。
下面是其它一些在添加依賴時可能用到的配置:
compile: 主module
androidTestCompile: 測試module
debugCompile: debug構建類型的編譯
releaseCompile: release構建類型的編譯
因為構建一個apk必然有一個相關的構建類型,所以apk通常至少有兩個編譯配置:compile和<buildtype>Compile
建立一個構建類型時會自動建立一個基於它名字的編譯配置<buildtype>Compile
當你在debug版本裡需要使用一個自訂庫(例如記錄crash資訊),而release版本不需要,或者他們依賴同一個庫的不同版本的時候,會非常有用。
也可以通過添加一個目錄來依賴目錄下的所有jar檔案:
compile fileTree(dir: 'libs', include: ['*.jar'])
4.1.2 遠程檔案
Gradle支援從Maven或者Ivy倉庫擷取依賴檔案。
首先,必須把倉庫添加到列表中,其次,必須按照Maven或者Ivy的檔案聲明規範來聲明依賴。
repositories { mavenCentral()}dependencies { compile 'com.google.guava:guava:11.0.2'}android { ...}
Note: mavenCentral()是指定倉庫URL的便捷方式。Gradle支援遠程和本地倉庫。
Note: Gradle遵循依賴關係的傳遞性。如果一個被依賴檔案也依賴其它檔案,那些被依賴的檔案也會被拉取下來。