原文地址:http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
6、 Build Variants(構建變種版本)
新構建系統的一個目標就是允許爲同一個應用創建不同的版本。
這裏有兩個主要的使用情景:
1、同一個應用的不同版本。例如一個免費的版本和一個收費的專業版本。
2、同一個應用需要打包成不同的apk以發佈Google Play Store。查看http://developer.android.com/google/play/publishing/multiple-apks.html獲取更多詳細信息。
3、綜合1和2兩種情景。
這個目標就是要讓在同一個項目裏生成不同的APK成爲可能,以取代以前需要使用一個庫項目和兩個及兩個以上的應用項目分別生成不同APK的做法。
6.1 Product flavors(不同定製的產品)
一個product flavor定義了從項目中構建了一個應用的自定義版本。一個單一的項目可以同時定義多個不同的flavor來改變應用的輸出。
這個新的設計概念是爲了解決不同的版本之間的差異非常小的情況。雖然最項目終生成了多個定製的版本,但是它們本質上都是同一個應用,那麼這種做法可能是比使用庫項目更好的實現方式。
Product flavor需要在productFlavors這個DSL容器中聲明:
android {
....
productFlavors {
flavor1 {
...
}
flavor2 {
...
}
}
}
這裏創建了兩個flavor,名爲flavor1和flavor2。
注意:flavor的命名不能與已存在的Build Type或者androidTest這個sourceSet有衝突。
6.2 Build Type + Product Flavor = Build Variant(構建類型+定製產品=構建變種版本)
正如前面章節所提到的,每一個Build Type都會生成一個新的APK。
Product Flavor同樣也會做這些事情:項目的輸出將會拼接所有可能的Build Type和Product Flavor(如果有Flavor定義存在的話)的組合。
每一種組合(包含Build Type和Product Flavor)就是一個Build Variant(構建變種版本)。
例如,在上面的Flavor聲明例子中與默認的debug和release兩個Build Type將會生成4個Build Variant:
* Flavor1 - debug
* Flavor1 - release
* Flavor2 - debug
* Flavor2 - release
項目中如果沒有定義flavor同樣也會有Build Variant,只是使用的是默認的flavor和配置。默認的flavor是沒有名字的,所以生成的Build Variant列表看起來就跟Build Type列表一樣。
6.3 Product Flavor Configuration(Product Flavor的配置)
每一個flavor都是通過閉包來配置的:
android {
...
defaultConfig {
minSdkVersion 8
versionCode 10
}
productFlavors {
flavor1 {
packageName "com.example.flavor1"
versionCode 20
}
flavor2 {
packageName "com.example.flavor2"
minSdkVersion 14
}
}
}
注意ProductFlavor類型的android.productFlavors.*對象與android.defaultConfig對象的類型是相同的。這意味着它們共享相同的屬性。
defaultConfig爲所有的flavor提供基本的配置,每一個flavor都可以重設這些配置的值。在上面的例子中,最終的配置結果將會是:
* flavor1
* packageName: com.example.flavor1
* minSdkVersion: 8
* versionCode: 20
* flavor2
* packageName: com.example.flavor2
* minSdkVersion: 14
* versionCode: 10
通常情況下,Build Type的配置會覆蓋其它的配置。例如,Build Type的packageNameSuffix會被追加到Product Flavor的packageName上面。
也有一些情況是一些設置可以同時在Build Type和Product Flavor中設置。在這種情況下,按照個別爲主的原則決定。
例如,signingConfig就這種屬性的一個例子。
signingConfig允許通過設置android.buildTypes.release.signingConfig來爲所有的release包共享相同的SigningConfig。也可以通過設置android.productFlavors.*.signingConfig來爲每一個release包指定它們自己的SigningConfig。
6.4 Sourcesets and Dependencies(源組件和依賴關係)
與Build Type類似,Product Flavor也會通過它們自己的sourceSet提供代碼和資源。
上面的例子將會創建4個sourceSet:
* android.sourceSets.flavor1:位於src/flavor1/
* android.sourceSets.flavor2:位於src/flavor2/
* android.sourceSets.androidTestFlavor1:位於src/androidTestFlavor1/
* android.sourceSets.androidTestFlavor2:位於src/androidTestFlavor2/
這些sourceSet用於與android.sourceSets.main和Build Type的sourceSet來構建APK。
下面的規則用於處理所有使用的sourceSet來構建一個APK:
* 多個文件夾中的所有的源代碼(src/*/java)都會合並起來生成一個輸出。
* 所有的Manifest文件都會合併成一個Manifest文件。類似於Build Type,允許Product Flavor可以擁有不同的的組件和權限聲明。
* 所有使用的資源(Android res和assets)遵循的優先級爲Build Type會覆蓋Product Flavor,最終覆蓋main sourceSet的資源。
* 每一個Build Variant都會根據資源生成自己的R類(或者其它一些源代碼)。Variant互相之間沒有什麼是共享的。
最終,類似Build Type,Product Flavor也可以有它們自己的依賴關係。例如,如果使用flavor來生成一個基於廣告的應用版本和一個付費的應用版本,其中廣告版本可能需要依賴於一個廣告SDK,但是另一個不需要。
dependencies {
flavor1Compile "..."
}
在這個例子中,src/flavor1/AndroidManifest.xml文件中可能需要聲明訪問網絡的權限。
每一個Variant也會創建額外的sourceSet:
* android.sourceSets.flavor1Debug:位於src/flavor1Debug/
* android.sourceSets.flavor1Release:位於src/flavor1Release/
* android.sourceSets.flavor2Debug:位於src/flavor2Debug/
* android.sourceSets.flavor2Release:位於src/flavor2Release/
這些sourceSet擁有比Build Type的sourceSet更高的優先級,並允許在Variant的層次上做一些定製。
6.5 Building and Tasks(構建和任務)
我們前面提到每一個Build Type會創建自己的assemble<name>task,但是Build Variant是Build Type和Product Flavor的組合。
當使用Product Flavor的時候,將會創建更多的assemble-type task。分別是:
1、assemble<Variant Name>:允許直接構建一個Variant版本,例如assembleFlavor1Debug。
2、assemble<Build Type Name>:允許構建指定Build Type的所有APK,例如assembleDebug將會構建Flavor1Debug和Flavor2Debug兩個Variant版本。
3、assemble<Product Flavor Name>:允許構建指定flavor的所有APK,例如assembleFlavor1將會構建Flavor1Debug和Flavor1Release兩個Variant版本。
另外assemble task會構建所有可能組合的Variant版本。
6.6 Testing(測試)
測試multi-flavors項目非常類似於測試簡單的項目。
androidTest sourceSet用於定義所有flavor共用的測試,但是每一個flavor也可以有它自己特有的測試。
正如前面提到的,每一個flavor都會創建自己的測試sourceSet:
* android.sourceSets.androidTestFlavor1:位於src/androidTestFlavor1/
* android.sourceSets.androidTestFlavor2:位於src/androidTestFlavor2/
同樣的,它們也可以擁有自己的依賴關係:
dependencies {
androidTestFlavor1Compile "..."
}
這些測試可以通過main的標誌性deviceCheck task或者main的androidTest task(當flavor被使用的時候這個task相當於一個標誌性task)來執行。
每一個flavor也擁有它們自己的task來這行這些測試:androidTest<VariantName>。例如:
* androidTestFlavor1Debug
* androidTestFlavor2Debug
同樣的,每一個Variant版本也會創建對應的測試APK構建task和安裝或卸載task:
* assembleFlavor1Test
* installFlavor1Debug
* installFlavor1Test
* uninstallFlavor1Debug
* ...
最終的HTML報告支持根據flavor合併生成。
下面是測試結果和報告文件的路徑,第一個是每一個flavor版本的結果,後面的是合併起來的結果:
* build/androidTest-results/flavors/<FlavorName>
* build/androidTest-results/all/
* build/reports/androidTests/flavors<FlavorName>
* build/reports/androidTests/all/
6.7 Multi-flavor variants
在一些情況下,一個應用可能需要基於多個標準來創建多個版本。例如,Google Play中的multi-apk支持4個不同的過濾器。區分創建的不同APK的每一個過濾器要求能夠使用多維的Product Flavor。
假如有個遊戲需要一個免費版本和一個付費的版本,並且需要在multi-apk支持中使用ABI過濾器(譯註:ABI,應用二進制接口,優點是不需要改動應用的任何代碼就能夠將應用遷移到任何支持相同ABI的平臺上)。這個遊戲應用需要3個ABI和兩個特定應用版本,因此就需要生成6個APK(沒有因計算不同Build Types生成的Variant版本)。
然而,注意到在這個例子中,爲三個ABI構建的付費版本源代碼都是相同,因此創建6個flavor來實現不是一個好辦法。
相反的,使用兩個flavor維度,並且自動構建所有可能的Variant組合。
這個功能的實現就是使用Flavor Groups。每一個Group代表一個維度,並且flavor都被分配到一個指定的Group中。
android {
...
flavorGroups "abi", "version"
productFlavors {
freeapp {
flavorGroup "version"
...
}
x86 {
flavorGroup "abi"
...
}
...
}
}
andorid.flavorGroups數組按照先後排序定義了可能使用的group。每一個Product Flavor都被分配到一個group中。
上面的例子中將Product Flavor分爲兩組(即兩個維度),爲別爲abi維度[x86,arm,mips]和version維度[freeapp,paidapp],再加上默認的Build Type有[debug,release],這將會組合生成以下的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.flavorGroups中定義的group排序非常重要(Variant命名和優先級等)。
每一個Variant版本的配置由幾個Product Flavor對象決定:
* android.defaultConfig
* 一個來自abi組中的對象
* 一個來自version組中的對象
flavorGroups中的排序決定了哪一個flavor覆蓋哪一個,這對於資源來說非常重要,因爲一個flavor中的值會替換定義在低優先級的flavor中的值。
flavor groups使用最高的優先級定義,因此在上面例子中的優先級爲:
abi > version > defaultConfig
Multi-flavors項目同樣擁有額外的sourceSet,類似於Variant的sourceSet,只是少了Build Type:
* android.sourceSets.x86Freeapp:位於src/x86Freeapp/
* android.sourceSets.armPaidapp:位於src/armPaidapp/
* etc...
這允許在flavor-combination的層次上進行定製。它們擁有過比基礎的flavor sourceSet更高的優先級,但是優先級低於Build Type的sourceSet。