上一章我們學習了Gralde的使用,創(chuàng)建和轉換Android工程。本章我們將深入了解構建文件,學習一些有用的tasks,探索Gradle和Android插件。
本章我們關注以下內容:
- 理解Gradle文件
- 學習使用構建任務
- 自定義構建
理解Gradle文件
在Android Studio中創(chuàng)建新工程的時候,默認生成3個Gradle文件:
MyApp
├── build.gradle
├── settings.gradle
└── app
└── build.gradle
這三個文件各有用途,我們在以下面進行探索。
settings.gradle
新的工程只包含一個app
模塊,settings.gradle
:
include ':app'
settings.gradle
文件在initialization期間執(zhí)行,定義了構建過程需要包含的模塊。上述代碼表示需要包含app
模塊。對于單模塊的工程,settings.gradle
文件不是必需的。多模塊的工程必須有該文件,否則Gradle不知道需要包含哪些模塊。
Gradle會為每個settings.gradle
文件創(chuàng)建一個Settings
對象,并觸發(fā)該對象相應的方法。
頂級build.gradle
在頂級build.gradle
中,你可以對工程中所有的模塊進行配置,它默認包含兩個代碼塊:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
allprojects {
repositories {
jcenter()
}
}
buildscript
用來配置當前的構建。
repositories
配置JCenter作為倉庫。我們在app或者library中可以使用JCenter這個依賴源提供的可供下載的各種庫。JCenter是一個廣泛使用的Maven倉庫。
dependencies
代碼塊用來配置構建過程需要的依賴。這意味著你不應該在頂級配置文件中包含你的applications或者libraries需要的依賴。Gradle Android插件是僅有的默認包含的插件。這對每個Android模塊都是必須的,因為該插件是運行Android相關任務的必要插件。
allprojects
代碼塊可以用來定義需要應用到所有模塊中的屬性。你還可以在該代碼塊中定義任務。這些任務可以用于所有的模塊。
一旦你使用了
allprojects
,modules就和project耦合到一起了。這意味著你不能脫離project的build.gradle
文件單獨構建每個模塊。起初看起來不是問題,但隨后你可能會把內部的library分離到單獨的project中,這時你就需要重構構建文件了。
模塊的build.gradle
模塊級別的build.gradle
文件包含應用于Android app模塊的配置。它也可以覆寫來自頂級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'
}
下面我們來分析3個主要的塊
Plugin
第一行引入了Android應用插件,它在頂級構建文件中被配置為一個依賴。Android插件由Google的Android Tools團隊開發(fā)和維護,提供了用于構建、測試和打包Android應用和庫的所有任務。
Android
android
代碼塊包含了整個Android特有的配置,由Android插件提供。必需的屬性僅僅是compileSdkVersion
和buildToolsVersion
:
- compileSdkVersion是Android的API版本
- buildToolsVersion是build tools的版本
build tools包含命令行工具,比如aapt,zipalign,dx和renderscript。你可以通過SDK Manager來下載build tools。
defaultConfig
代碼塊配置了app的核心屬性。該塊中的屬性覆蓋了AndroidManifest.xml
文件中相應的屬性:
defaultConfig {
applicationId "com.gradleforandroid.gettingstarted"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
applicationId
覆寫了manifest文件中的package
(包名),但是兩者又有些不同。在Gradle作為默認的Android構建系統(tǒng)之前,AndroidManifest.xml
中的包名有兩個功能:
- 作為app的唯一標識
- 作為R.java文件的包名
Gradle利用構建變量使創(chuàng)建不同版本的app變得簡單了許多。比如你可以非常簡單的創(chuàng)建一個free版本和一個paid版本。這兩個版本需要有不同的app標識,這樣它們才能同時存在于Google應用商店,同時被安裝到手機上。然而源代碼和生成的R.java文件必須時刻保持同樣的包名。否則你所有的源代碼都需要改變。這就是Android Tools團隊將包名這兩個功能分離開的原因。manifest文件中的package
繼續(xù)作為R.java文件的包名,而設備和Google Play所需的app標識則由applicationId替代。在我們體驗build types之后,applicationId
將更加有趣。
defaultConfig
接下來的兩個屬性是minSdkVersion
和targetSdkVersion
。這兩個屬性應該比較熟悉了,它們定義在manifest文件的<uses-sdk>
元素中。minSdkVersion
用來配置運行app的最低的API。targetSdkVersion
告知測試app需要的特定Android版本,這樣操作系統(tǒng)不需要啟用任何正向兼容性行為。這與我們之前看到的compileSdkVersion
無關。
versionCode
和versionName
也與manifest中的一致。
構建文件的所有值都會替換manifest文件的值。所以如果你在build.gradle
文件定義了這些值,就沒有必要在manifest文件再次定義。只有當構建文件沒有定義這些值的時候,manifest中的值才會起作用。
buildTypes
塊是你定義如何構建和打包不同app構建類型的地方。我們將在第四章詳細學習構建類型。
dependencies
dependencies
塊是Gradle標準配置的一部分(這就是它為什么放在了android
塊的外部的原因),定義了app或者library所需的全部依賴。Android app默認將libs
文件夾作為全部JAR依賴文件的目錄。取決于你在新工程向導中勾選的選項,它也可能依賴AppCompat
庫。我們將在第三章討論依賴。
開始學習任務
為了得知工程中有多少可用任務,你可以運行gradlew tasks
命令。在一個新創(chuàng)建的工程中,這些可用任務包含Android tasks,build tasks,build setup tasks,help tasks,install tasks,verification tasks和其他tasks。如果你還想看到任務間的依賴關系,可以運行gradlew tasks --all
命令。演示一個任務也是可以的,它可以打印出執(zhí)行一項任務所需的所有步驟。演示不會真正去執(zhí)行這些步驟,它是一種查看運行特定任務結果的安全方式。你可以通過添加參數-m
或者--dry-run
來演示任務。
基礎任務
Gradle Android插件使用了Java基礎插件,Java基礎插件又使用了基礎插件。這些添加了標準生命周期任務和一些通用屬性。基礎插件定義了assemble
和clean
任務,Java基礎插件定義了check
和build
任務。這些任務并沒有在基礎插件中實現,不包含任何的actions;它們用來定義添加執(zhí)行作業(yè)的實際tasks的規(guī)則。
這些任務的規(guī)則有:
- assemble收集工程的輸出
- clean清理工程的輸出
- check運行所有的檢查,通常是單元測試和設備測試
-
build運行
assemble
和check
Java基礎插件也添加了source sets的概念。Android插件建立在這些約定之上,從而暴露了那些有經驗的Gradle用戶習慣于看到的任務。在這些基礎任務之上,Android插件也添加了許多Android特有的任務。
Android任務
Android插件擴展、實現了基礎任務。下面是在Android環(huán)境中這些任務的功能:
- assemble為每一個build type 創(chuàng)建一個APK
- clean刪除所有構建結果,包括APK文件
- check運行Lint檢查,并在查到問題時停止構建
- build運行assemble和check
assemble
任務默認依賴于assembleDebug
和assembleRelease
,在你添加更多build type時,它會依賴于更多任務。這意味著運行assembe
將構建每一個build type。
除了擴展這些任務,Android插件也添加了一些任務。下面是最重要的一些任務:
- connectedCheck在連接的真機或者虛擬器上運行測試
- deviceCheck為其他插件在遠程設備運行測試提供的接口
- installDebug、installRelease在設備或者虛擬器上安裝特定版本
- uninstall每一個install任務有對應的uninstall任務
build
任務依賴于check
,但不依賴connectedCheck
和deviceCheck
。這是為了確保常規(guī)檢查不要求有連接的設備或者運行的模擬器。運行check
任務會生成一個Lint報告,包含所有的warnings和errors,并有相應的解釋和文檔鏈接。這個報告在app/build/outputs
目錄,名為lint-results.html
。
在你assemble release時,Lint會檢查引起app崩潰的問題。在它發(fā)現問題時,它將停止構建,并在命令行中打印錯誤。Lint也會在app/build/outputs
目錄生成一個名為lint-results-release-fatal.html
的報告。如果你有多個問題,瀏覽HTML報告比查看命令行更加方便。報告提供的鏈接也很有用,它們會指向問題的細節(jié)。
Inside Android Studio
使用gradle窗口查看任務。本節(jié)略過。
自定義構建
Android Studio的構建。
操作manifest文件的條目
我們前面已經見過在構建文件中配置applicationId, minSdkVersion,targetSdkVersion,versionCode和versionName
。你還可以操作一些其他的屬性:
- testApplicationId設備測試APK的applicationId
- testInstrumentationRunner運行測試所用到的JUnit測試器的名稱(見第六章,運行測試)
- signingConfig(見第四章,創(chuàng)建構建變體)
- proguardFile proguardFiles(見第九章,高級自定義構建)
Inside Android Studio
使用窗口配置工程,本節(jié)略過。
BuildConfig和resources
自SDK tools R17以來,build tools便會生成一個BuildConfig
類,它包含了根據build type設置的DEBUG
常量。當你只想在debug下運行一些代碼時,這將會很有用,比如打印日志等。Gradle允許你擴展該文件,這樣你就可以在debug和release版本擁有不同的常量了。
這些常量在開關某些特性或者設置服務器地址時,非常有用,比如:
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"
}
}
}
\"
中的轉義字符是必需的,這樣才能生成實際的字符串。添加了buildConfigField
之后,你就可以在Java代碼中使用BuildConfig.API_URL
和BuildConfig.LOG_HTTP
這兩個值了。
最近,Android Tools團隊還添加了配置resources資源的特性:
android {
buildTypes {
debug {
resValue "string", "app_name", "Example DEBUG"
}
release {
resValue "string", "app_name", "Example"
}
}
}
這里不需要為Example DEBUG
和Example
兩個值添加\"
轉義的雙引號,因為資源值默認用value=""
包裝。
工程范圍的設置
如果你的工程中有多個模塊,為所有模塊添加統(tǒng)一配置而非手動去配置每一個模塊會顯得非常有用。我們已經見過如何在頂級構建文件中使用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
塊是定義額外屬性的一種方式。你可以利用屬性自定義構建過程,我們將在第七章講述如何利用它們來自定義任務。有幾種方式可以定義屬性,這里我們著重介紹如下三種:
-
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
本例中我們創(chuàng)建了一個新任務。我們將在第七章詳解任務語法
在命令行中運行printProperties
任務,得到如下結果:
$ gradlew printProperties -P cmd='Hello from the command line'
:printProperties
Hello from build.gradle
Hello from gradle.properties
Hello from the command line
有了自定義屬性,修改構建配置就和修改單個屬性一樣簡單。
在頂級構建文件和模塊級構建文件中定義屬性都是可以的。模塊級構建文件中的同名屬性會覆蓋頂級構建文件的屬性
默認任務
如果你只是運行Gradle,并不明確指定某個任務,它將會打印一些如何使用Gradle的信息。這是因為help
任務是Gradle的默認任務。你也可以重寫默認任務,指定一個或者多個任務為默認任務,這樣就可以只運行Gradle而不指定具體任務了。
為了指定默認任務,你需要在頂級構建文件中添加如下代碼:
defaultTasks 'clean', 'assembleDebug'
現在當你運行Gradle Wrapper而不指定具體任務時,將會運行clean
和assembleDebug
任務。你也可以輸入如下代碼查看當前的默認任務:
$ gradlew tasks | grep "Default tasks"
Default tasks: clean, assembleDebug