Gradle For Android系列2:自定義Build配置
在上一章節中我們學習了Gradle的用法,以及如何建立Android專案以及如何從Eclipse中將專案轉換到Android Studio中。這一章節將介紹構建檔案配置的更多細節,以及一些有用的構建任務,並深入Gradle的Android外掛。
在本章中,我們將討論以下主題:
- 理解Gradle的各種配置檔案
- 初識Build Tasks
- 自定義構建
理解Gradle的各種配置檔案
當使用Android Studio建立新專案時,預設會生成三個Gradle檔案。 其中兩個檔案,settings.gradle和build.gradle,在專案的根目錄。另一個build.gradle檔案在Android應用程式模組中。 如下圖展示了Gradle檔案如何放置在專案中:
MyApp
|-buid.gradle
|-settings.gradle
|-app
|-buid.grade
這三個檔案每個都有其單獨的用途,接下來將詳細介紹:
settings.gradle檔案
對於只包含Android應用模組的專案,settings.gradle檔案內容如下所示
include ':app'
setting檔案在Build初始化階段執行,定義了哪些模組應改被包含在構建中。 在此示例中,應用程式模組被包含其中。單個模組專案不一定需要setting檔案,但是多模組專案必須要包含setting檔案;否則,Gradle不知道要將哪些模組包含到構建中。
setting檔案的幕後執行機制是:Gradle為每個設定檔案建立一個Settings物件,並從該物件呼叫必要的方法。你不需要知道Settings類的具體細節,但是知道有settings物件存在對深入瞭解gradle是有幫助的。
Settings類的完整解釋超出了本文件的範圍。 如果你想知道更多,你可以在Gradle官方文件中找到很多資訊。
專案根build.gradle檔案
在專案根目錄的build.gradle檔案中,你可以配置需要應用於專案中所有模組的選項。它預設包含兩個程式碼塊:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
allprojects {
repositories {
jcenter()
}
}
buildscript程式碼塊是配置實際構建的位置。我們在第1章Gradle和Android Studio入門中簡要討論了這一點。可以看到我們在repositories{}中將JCenter配置為依賴庫。依賴庫意味著依賴的來源,或者換句話說,一個可下載的第三方庫列表,我們可以在我們的應用和庫中使用。(JCenter是一個著名的Maven依賴庫)
dependencies{}用於為構建過程本身配置依賴。這意味著你不應在頂級構建檔案中包含你的應用程式或庫所需的依賴關係。當前預設定義了唯一的一個依賴是Gradle的Android外掛。Android外掛是每個Android模組構建所必須的,因為這個外掛定義了Android程式的具體構建流程,包含了可以執行的Android相關任務。
allprojects{}可用於定義需要應用於所有模組的屬性。你可以在allprojects{}建立任務,那麼這些任務將在所有模組中都可用。
因為allprojects是將其中的配置應用到所有模組中,如果不是公共的通用配置,而是單獨於某一個模組中的配置,最好是放在模組中的build檔案中。
模組的build.gradle檔案
模組級的build.gradle檔案包含僅適用於Android應用程式或庫模組的選項。它可以直接覆蓋頂級build.gradle檔案中的任何配置選項。模組build.gradle檔案內容如下所示:
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.gradleforandroid.gettingstarted"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile
('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
}
接下來詳細介紹各個配置
Plugin
第一行程式碼應用Android應用程式外掛,在前面我們介紹過,Android外掛被專案根構建檔案配置為build過程的依賴。Android外掛提供構建,測試和打包Android應用程式和庫所需的所有任務,現由Google Android Tools Team編寫和維護。
Android
構建檔案的最大部分是android塊。 此塊包含整個Android特定的構建配置。
android配置塊中至少需要配置屬性是compileSdkVersion和buildToolsVersion:
- compileSdkVersion:編譯Android程式的sdk版本
- buildToolsVersion:編譯過程中使用的android構建工具的版本
構建工具包含各種命令列程式,例如aapt,zipalign,dx和renderscript; 它們用於生成構成應用程式的各種中介軟體。你可以通過SDK Manager下載這些構建工具(預設sdk會有一個版本的構建工具)。
defaultConfig配置了應用程式的核心屬性。此塊中的屬性將覆蓋AndroidManifest.xml檔案中的相應條目:
defaultConfig {
applicationId "com.gradleforandroid.gettingstarted"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
此塊中的第一個屬性是applicationId。這將覆蓋manifest檔案中的包名稱,但applicationId和應用包名之間有一些差異。在Gradle用作預設Android構建系統之前,AndroidManifest.xml中的包名稱有兩個目的:一是用作應用程式的唯一識別符號,再就是用作R資源類中包的名稱。假設有如下場景:你要釋出一款app,有一個免費版本和付費版本。並且這兩個版本需要具有單獨的識別符號,因為它們在Google Play商店中需要顯示為不同的應用,並且可以同時安裝。然而,在未使用Gradle構建之前,原始碼和生成的R類必須始終保持相同的包名稱,因此,在建立不同的版本時,所有的原始檔將需要更改。在使用Gradle之後applicationId和包名區分開了,這樣你可以隨意更改專案的應用Id而不必更改原始碼的包名。manifest檔案中定義的程式包名繼續在原始碼和R類中使用,而裝置和Google Play使用applicationId作為唯一識別符號。
defaultConfig中接下來的兩個屬性是minSdkVersion和targetSdkVersion。這兩個應該看起來很熟悉,因為它們在manifest檔案中被定義為元素的一部分。 minSdkVersion設定用於配置執行應用程式所需的最低API級別。 targetSdkVersion設定通知系統該應用程式在特定版本的Android上測試,並且作業系統不需要啟用任何向前相容性行為。這與我們之前看到的compileSdkVersion無關。
versionCode和versionName也具有與manifest檔案中相同的功能,併為您的應用定義版本號和使用者友好的版本名稱。
構建檔案中的所有值都將覆蓋清單檔案中的值。因此,如果在build.gradle中定義它們,則不需要在清單檔案中定義它們。如果構建檔案不包含值,則使用manifest檔案中定義的值。
buildTypes塊是你定義如何構建和打包應用程式的不同構建型別(Debug或者Release)的地方。我們將在第4章:建立構建變體中詳細討論構建Build Variant。
Dependencies
依賴塊是標準Gradle配置的一部分(這也是為什麼它放在了android塊之外),並定義了應用模組或庫模組的所有依賴關係。預設情況下,新的Android應用程式依賴於libs目錄中的所有JAR檔案和相應的support包下的庫。 我們將在第3章 管理依賴關係中討論依賴關係。
初識Gradle Task
要知道專案中有哪些任務可用,可以執行gradlew tasks,這將打印出所有可用任務的列表。 在新建立的Android專案中,這包括Android Tasks,Build Tasks,Build Setup Task,Help Task,Install Task,Verification Task和和其他任務。 如果你不僅要看到任務,還要看到它們的依賴,你可以執行gradlew tasks –all。 可以執行dry執行任務,列印特定任務時執行的所有步驟。這個dry執行實際上不會執行任何這些步驟,因此它是一個安全的方式來檢視當執行某個任務時是否按照你所期待的那樣執行。你可以通過新增引數-m或–dry-run來進行dry執行。
基礎任務(Base tasks)
Gradle的Android外掛使用Java基本外掛,同時也使用Android基本外掛。這些基本外掛添加了標準生命週期任務和一些常見的約定屬性。Android基本外掛定義了assemble任務和clean任務,Java基本外掛定義了check任務和clean任務。這些任務並不在基本外掛中實現,並且不執行任何操作; 基本外掛只是用於定義外掛的約定,用於添實際任務加執行工作的規範。
這些任務的約定是:
- assemble 將專案整合構建並輸出
- clean 將專案輸入完全清除
- check 執行所有檢查,通常包括單元測試和功能測試
- build 執行assemble任務和check任務
Java基礎外掛也添加了soruce set的概念。 Android外掛基於這些約定,有經驗的Gradle使用者根據任務名稱就能一眼分辨出該任務是什麼型別的。除了這些基本任務之外,Android外掛還添加了許多Android特定的任務。
Android任務
Android外掛擴充套件了基本任務並實現了它們的行為。如下為各個任務在Android環境中對應的作用:
- assemble 為每一種構建型別建立APK
- clean 移除構建產生的各類檔案,如:apk
- check lint檢查,如果lint檢查失敗就會中斷構建
- build 執行assemble和check
assemble任務預設情況下依賴於assembleDebug和assembleRelease,如果新增更多build type,則會依賴更多需要更多build type的assemble任務。這意味著執行assemble將觸發一個構建的每一個你有的build type。
除了擴充套件這些任務,Android外掛還添加了一些新的。這些是最重要的新任務:
- connectedCheck 在模擬器或真機上執行UI測試
- deviceCheck 其他外掛在遠端裝置上執行測試的佔位任務
- installDebug/installRelease 給連線上的模擬器或真機上安裝指定版本Apk
- 所有install任務都有對應的uninstall任務
build任務取決於check任務,但不包括connectedCheck或deviceCheck。這是為了確保常規檢查不需要連線真機裝置或執行模擬器。執行check任務將生成一個包含所有警告和錯誤列表的Lint報告,以及詳細說明和指向相關文件的連結。此報告可以在app/build/outputs中找到,名為lint-results.html。它看起來像這樣:
當你assemble一個版本,Lint會檢查一些可能會導致應用程式崩潰的嚴重錯誤問題。一旦發現問題,它將中止構建並將錯誤列印到命令列介面。Lint還將在app/build/outputs中生成一個名為lint-results-release-fatal.html的報告。如果構建發現有多個問題,通過HTML報告會比在命令列介面中的展示效果更好,每個問題提供的連結也非常有用,因為上面會帶有詳細解釋問題。
Android Studio整合
你不用總是從命令列介面執行Gradle任務。Android Studio有一個工具視窗,其中包含所有可用任務的列表。 這個工具視窗稱為Gradle,如下所示:
從此工具視窗中,可以通過雙擊其名稱來執行任務。你可以在Gradle Console工具視窗中跟蹤任何正在執行的任務的進度。如果找不到這些工具視窗,可以在Tool Window下的View選單中開啟它們。 這就是Gradle Console工具視窗的樣子:
你還可以從Android Studio中的命令列介面執行任務,以便你可以根據需要在IDE中執行所有與應用相關的工作。要執行該命令,需要開啟終端工具視窗。這是一個完整的終端,所以可以執行任何命令。 你可能需要首先導航到專案的根目錄,以便使用gradlew命令。
更改Android Studio終端
可以將Android Studio中的終端配置為使用不同的shell。 例如,在Windows上,終端預設為命令提示符。 如果你喜歡使用Git Bash(或任何其他shell),開啟Android Studio設定(在檔案和設定下),並尋找終端。 在那裡你可以改變shell路徑。 對於Windows上的Git Bash,它看起來像這樣:C\Program Files(x86)\Git\bin\sh.exe –login -i。
自定義構建
有很多方法來自定義構建過程,當你在Android Studio中編輯build.gradle檔案時,建議始終將專案與Gradle檔案同步,無論你要自定義什麼。這在新增依賴項或BuildConfig欄位時變得尤為重要,我們將在稍後討論。
一旦您編輯settings,gradle或build.gradle檔案時,Android Studio就會在編輯器中顯示一條訊息,提示需要sync專案。你可以通過導航到Tools | Android | Sync Project with Gradle或工具欄中的相應sync按鈕來手動同步。
Sync操作原理:Android Studio Sync實際上會基於構建檔案中的配置執行generateDebugSources任務來生成所有必需的類。
Manifest屬性操作
我們已經看到,可以直接從構建檔案而不是在清單檔案中配置applicationId,minSdkVersion,targetSdkVersion,versionCode和versionName。 這裡還有一些其他屬性可以操作:
- testApplicationId 測試Apk的applicationId
- testInstrumentationRunner 應用運城JUnit測試的測試Runner名稱
- signingConfig 打包簽名配置
- proguardFile/proguardFiles 混淆配置
BuildConfig和Resources
從SDK tool revision 17開始,構建工具會生成一個名為BuildConfig的類,其中包含根據Build Type設定的DEBUG常數(BuildType是Debug時,DEBUG為true,反之為false)。如果你希望只有在除錯狀態時執行指定的程式碼(例如日誌記錄),這將非常有用。它可以通過Gradle擴充套件該檔案,以便可以在Debug和Release中包含不同值的常量。
這些常數可用於切換功能或設定伺服器URL,例如:
android {
buildTypes {
debug {
buildConfigField "String", "API_URL", "\"http://test.example.com/api\""
buildConfigField "boolean", "LOG_HTTP_CALLS", "true"
}
release {
buildConfigField "String", "API_URL", "\"http://example.com/api\""
buildConfigField "boolean", "LOG_HTTP_CALLS", "false"
}
}
}
API_URL為了生成為實際字串值,在引用是必須在雙引號上加上轉義符。新增buildConfigField行後,可以在實際的Java程式碼中使用BuildConfig.API_URL和BuildConfig.LOG_HTTP_CALLS常量。
Android Tools Team還增加了以類似方式配置資源:
android {
buildTypes {
debug {
resValue "string", "app_name", "Example DEBUG"
}
release {
resValue "string", "app_name", "Example"
}
}
}
在這裡不需要轉義的雙引號,因為資源值在預設情況下總是用value =”“形式存在的。
Project設定
如果在一個專案中有多個Android模組,則可以將settings應用於所有模組,而無需手動更改每個模組的構建檔案。我們已經看到了如何在生成的頂級構建檔案中使用allprojects塊來定義依賴庫,我們也可以使用相同的策略來應用Android特定的設定:
allprojects {
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
}
}
上面的程式碼中,如果專案所有的模組都是Android應用程式模組,程式碼才會生效,因為你需要應用Android外掛來訪問Android應用程式特定的設定。全域性引用更好的實踐模式是在專案根構建檔案中定義值,然後在各個模組中引用它們。 在Gradle中可以在Project物件上新增額外的特別屬性。這意味著任何build.gradle檔案都可以在ext塊定義額外的屬性。
你可以將具有自定義屬性的ext塊新增到專案根構建檔案中:
ext {
compileSdkVersion = 22
buildToolsVersion = "22.0.1"
}
這使得可以使用rootProject在模組級構建檔案中使用屬性:
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
}
專案屬性
前面示例中的ext塊是定義額外屬性的一種方法。 你也可以使用屬性來自定義構建過程,我們將在第7章“建立任務和外掛”中編寫自定義任務時使用它們。有幾種方法來定義屬性,但我們現在先了解三個最常用的方法:
- ext程式碼塊
- gradle.properties檔案
- 命令列的 -P引數
下面是一個build.gradle檔案的示例,它包含了新增額外屬性的三種方式:
ext {
local = 'Hello from build.gradle'
}
task printProperties << {
println local // Local extra property
println propertiesFile // Property from file
if (project.hasProperty('cmd')) {
println cmd // Command line property
}
}
這是對應的gradle.properties檔案內容(在同一資料夾中):
propertiesFile = Hello from gradle.properties
在示例中,我們建立一個新任務。 我們將在第7章“建立任務和外掛”中討論任務和解釋相關語法。
如果使用命令列執行printProperties任務,輸出將如下所示:
$ gradlew printProperties -P cmd='Hello from the command line'
:printProperties
Hello from build.gradle
Hello from gradle.properties
Hello from the command line
自定義屬性使得更改構建的配置與更改單個屬性一樣簡單,甚至只需新增命令列引數即可(命令列-P方式)。
可以在專案根目錄構建檔案和模組構建檔案中都定義屬性。 果模組定義了頂級檔案中已存在的屬性,那麼它將覆蓋根目錄構建檔案中定義的。
預設任務
如果你執行Gradle而不指定任務,它會預設執行help任務,列印一些關於如何使用Gradle的資訊。 這是因為幫助任務設定為預設任務。你可以用一個你的常用任務或多個任務覆蓋預設任務。每次執行Gradle,不用明確指定任務就可執行(相當於是一個熱鍵)。
要指定預設任務,請將此行新增到專案根build.gradle檔案中:
defaultTasks 'clean', 'assembleDebug'
現在,當你執行沒有任何引數的Gradlew命令時,它將執行clean和assembleDebug任務。通過執行tasks任務並過濾輸出,很容易看到哪些任務被設定為預設任務。
$ gradlew tasks | grep "Default tasks"
Default tasks: clean, assembleDebug
總結
在本章中,我們詳細介紹了Android Studio建立新專案時自動生成的不同Gradle檔案。 你現在可以自己建立build.gradle檔案,並新增所有必需的配置屬性。
我們開始了基本的構建任務,並瞭解Android外掛如何基於基本外掛來構建專案,以及如何擴充套件新的Android任務。 我們還看到了如何從命令列介面和從Android Studio內部執行構建任務。
在過去幾年中,Android開發人員生態系統已經發展得非常迅速,許多有趣的第三方庫已經可供大家使用。 在下一章中,我們將介紹幾種向專案新增依賴項的方法。