原文地址: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。