新構(gòu)建系統(tǒng)的一個(gè)目標(biāo)就是允許為同一個(gè)應(yīng)用創(chuàng)建不同的版本。
這里有兩個(gè)主要的使用情景:
這個(gè)目標(biāo)就是要讓在同一個(gè)項(xiàng)目里生成不同的APK成為可能,以取代以前需要使用一個(gè)庫項(xiàng)目和兩個(gè)及兩個(gè)以上的應(yīng)用項(xiàng)目分別生成不同APK的做法。
一個(gè)product flavor定義了從項(xiàng)目中構(gòu)建了一個(gè)應(yīng)用的自定義版本。一個(gè)單一的項(xiàng)目可以同時(shí)定義多個(gè)不同的flavor來改變應(yīng)用的輸出。
這個(gè)新的設(shè)計(jì)概念是為了解決不同的版本之間的差異非常小的情況。雖然最項(xiàng)目終生成了多個(gè)定制的版本,但是它們本質(zhì)上都是同一個(gè)應(yīng)用,那么這種做法可能是比使用庫項(xiàng)目更好的實(shí)現(xiàn)方式。
Product flavor需要在productFlavors
這個(gè)DSL容器中聲明:
android {
....
productFlavors {
flavor1 {
...
}
flavor2 {
...
}
}
}
這里創(chuàng)建了兩個(gè)flavor,名為flavor1
和flavor2
。
注意:
flavor的命名不能與已存在的_Build Type_或者androidTest
這個(gè)_sourceSet_有沖突。
正如前面章節(jié)所提到的,每一個(gè)_Build Type_都會(huì)生成一個(gè)新的APK。
_Product Flavor_同樣也會(huì)做這些事情:項(xiàng)目的輸出將會(huì)拼接所有可能的_Build Type_和Product Flavor(如果有Flavor定義存在的話)的組合。
每一種組合(包含_Build Type_和Product Flavor)就是一個(gè)Build Variant(構(gòu)建變種版本)。
例如,在上面的Flavor聲明例子中與默認(rèn)的debug
和release
兩個(gè)_Build Type_將會(huì)生成4個(gè)Build Variant:
項(xiàng)目中如果沒有定義flavor同樣也會(huì)有Build Variant,只是使用的是默認(rèn)的flavor和配置。default
(默認(rèn))的flavor/config是沒有名字的,所以生成的Build Variant列表看起來就跟_Build Type_列表一樣。
每一個(gè)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提供基本的配置,每一個(gè)flavor都可以重設(shè)這些配置的值。在上面的例子中,最終的配置結(jié)果將會(huì)是:
* `flavor1`
* `packageName`: com.example.flavor1
* `minSdkVersion`: 8
* `versionCode`: 20
* `flavor2`
* `packageName`: com.example.flavor2
* `minSdkVersion`: 14
* `versionCode`: 10
通常情況下,_Build Type_的配置會(huì)覆蓋其它的配置。例如,_Build Type_的packageNameSuffix
會(huì)被追加到_Product Flavor_的packageName
上面。
也有一些情況是一些設(shè)置可以同時(shí)在_Build Type_和_Product Flavor_中設(shè)置。在這種情況下,按照個(gè)別為主的原則決定。
例如,signingConfig
就這種屬性的一個(gè)例子。
signingConfig
允許通過設(shè)置android.buildTypes.release.signingConfig
來為所有的release
包共享相同的SigningConfig。也可以通過設(shè)置android.productFlavors.*.signingConfig
來為每一個(gè)release包指定它們自己的SigningConfig。
與_Build Type_類似,_Product Flavor_也會(huì)通過它們自己的_sourceSet_提供代碼和資源。
上面的例子將會(huì)創(chuàng)建4個(gè)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_來構(gòu)建APK。
下面的規(guī)則用于處理所有使用的sourceSet來構(gòu)建一個(gè)APK:
main
_sourceSet_的資源。最終,類似Build Type,_Product Flavor_也可以有它們自己的依賴關(guān)系。例如,如果使用flavor來生成一個(gè)基于廣告的應(yīng)用版本和一個(gè)付費(fèi)的應(yīng)用版本,其中廣告版本可能需要依賴于一個(gè)廣告SDK,但是另一個(gè)不需要。
dependencies {
flavor1Compile "..."
}
在這個(gè)例子中,src/flavor1/AndroidManifest.xml文件中可能需要聲明訪問網(wǎng)絡(luò)的權(quán)限。
每一個(gè)Variant也會(huì)創(chuàng)建額外的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更高的優(yōu)先級,并允許在Variant的層次上做一些定制。
我們前面提到每一個(gè)_Build Type_會(huì)創(chuàng)建自己的assemble< name >task,但是_Build Variant_是_Build Type_和_Product Flavor_的組合。
當(dāng)使用_Product Flavor_的時(shí)候,將會(huì)創(chuàng)建更多的assemble-type task。分別是:
assembleFlavor1Debug
。assembleDebug
將會(huì)構(gòu)建Flavor1Debug和Flavor2Debug兩個(gè)Variant版本。assembleFlavor1
將會(huì)構(gòu)建Flavor1Debug和Flavor1Release兩個(gè)Variant版本。另外assemble
task會(huì)構(gòu)建所有可能組合的Variant版本。
測試multi-flavors項(xiàng)目非常類似于測試簡單的項(xiàng)目。
androidTest
_sourceSet_用于定義所有flavor共用的測試,但是每一個(gè)flavor也可以有它自己特有的測試。
正如前面提到的,每一個(gè)flavor都會(huì)創(chuàng)建自己的測試sourceSet:
android.sourceSets.androidTestFlavor1
位于src/androidTestFlavor1/android.sourceSets.androidTestFlavor2
位于src/androidTestFlavor2/同樣的,它們也可以擁有自己的依賴關(guān)系:
dependencies {
androidTestFlavor1Compile "..."
}
這些測試可以通過main的標(biāo)志性deviceCheck
task或者main的androidTest
task(當(dāng)flavor被使用的時(shí)候這個(gè)task相當(dāng)于一個(gè)標(biāo)志性task)來執(zhí)行。
每一個(gè)flavor也擁有它們自己的task來這行這些測試:androidTest< VariantName >。例如:
androidTestFlavor1Debug
androidTestFlavor2Debug
同樣的,每一個(gè)Variant版本也會(huì)創(chuàng)建對應(yīng)的測試APK構(gòu)建task和安裝或卸載task:
assembleFlavor1Test
installFlavor1Debug
installFlavor1Test
uninstallFlavor1Debug
最終的HTML報(bào)告支持根據(jù)flavor合并生成。 下面是測試結(jié)果和報(bào)告文件的路徑,第一個(gè)是每一個(gè)flavor版本的結(jié)果,后面的是合并起來的結(jié)果:
在一些情況下,一個(gè)應(yīng)用可能需要基于多個(gè)標(biāo)準(zhǔn)來創(chuàng)建多個(gè)版本。例如,Google Play中的multi-apk支持4個(gè)不同的過濾器。區(qū)分創(chuàng)建的不同APK的每一個(gè)過濾器要求能夠使用多維的Product Flavor。
假如有個(gè)游戲需要一個(gè)免費(fèi)版本和一個(gè)付費(fèi)的版本,并且需要在multi-apk支持中使用ABI過濾器(譯注:ABI,應(yīng)用二進(jìn)制接口,優(yōu)點(diǎn)是不需要改動(dòng)應(yīng)用的任何代碼就能夠?qū)?yīng)用遷移到任何支持相同ABI的平臺(tái)上)。這個(gè)游戲應(yīng)用需要3個(gè)ABI和兩個(gè)特定應(yīng)用版本,因此就需要生成6個(gè)APK(沒有因計(jì)算不同_Build Types_生成的Variant版本)。 然而,注意到在這個(gè)例子中,為三個(gè)ABI構(gòu)建的付費(fèi)版本源代碼都是相同,因此創(chuàng)建6個(gè)flavor來實(shí)現(xiàn)不是一個(gè)好辦法。 相反的,使用兩個(gè)flavor維度,并且自動(dòng)構(gòu)建所有可能的Variant組合。
這個(gè)功能的實(shí)現(xiàn)就是使用Flavor Groups。每一個(gè)Group代表一個(gè)維度,并且flavor都被分配到一個(gè)指定的Group中。
android {
...
flavorGroups "abi", "version"
productFlavors {
freeapp {
flavorGroup "version"
...
}
x86 {
flavorGroup "abi"
...
}
...
}
}
andorid.flavorGroups
數(shù)組按照先后排序定義了可能使用的group。每一個(gè)_Product Flavor_都被分配到一個(gè)group中。
上面的例子中將_Product Flavor_分為兩組(即兩個(gè)維度),為別為abi維度[x86,arm,mips]和version維度[freeapp,paidapp],再加上默認(rèn)的_Build Type_有[debug,release],這將會(huì)組合生成以下的Build Variant:
android.flavorGroups
中定義的group排序非常重要(Variant命名和優(yōu)先級等)。
每一個(gè)Variant版本的配置由幾個(gè)Product Flavor
對象決定:
flavorGroups中的排序決定了哪一個(gè)flavor覆蓋哪一個(gè),這對于資源來說非常重要,因?yàn)橐粋€(gè)flavor中的值會(huì)替換定義在低優(yōu)先級的flavor中的值。
flavor groups使用最高的優(yōu)先級定義,因此在上面例子中的優(yōu)先級為:
abi > version > defaultConfig
Multi-flavors項(xiàng)目同樣擁有額外的sourceSet,類似于Variant的sourceSet,只是少了Build Type:
android.sourceSets.x86Freeapp
位于src/x86Freeapp/android.sourceSets.armPaidapp
位于src/armPaidapp/這允許在flavor-combination的層次上進(jìn)行定制。它們擁有過比基礎(chǔ)的flavor sourceSet更高的優(yōu)先級,但是優(yōu)先級低于Build Type的sourceSet。