5 构建变种(Build Variants)
新构建系统的一个目标是使同一个应用可以创建不同的版本。
有两种主要的使用情况:
- 同一个应用的不同版本。例如:一个免费/示例版本与收费的专业版本。
- 同一个应用打包成多个不同APK发布在Google Play Store。查看http://developer.android.com/google/play/publishing/multiple-apks.html获取更多信息。
- 1和2的组合。
该目标使得同一个项目能够生成这些不同的APK,而不是通过使用一个库项目和2个以上的应用项目。
5.1 产品标识(Product flavors)
一个产品标识(Product Flavor)定义了从项目中构建一个应用的定制版本。一个项目可以有不同的标识(flavor)来变更生成的应用。
这个新概念旨在帮助应用之间差异非常小的项目进行构建。如果问题“这是同一个应用吗?”的答案是“是”的话,那么可能需要去复习下库项目的知识。
产品标识(Product Flavor)使用productFlavors
DSL容器来声明:
android {
...
productFlavors {
flavor1 {
...
}
flavor2 {
...
}
}
}
这里创建了两个标识(flavor),名为flavor1
和flavor2
。
注意:标识(flavor)的名称不能与已存在的构建类型(Build Type)名称,或者androidTest
和test
源码集合(sourceSet)冲突。
5.2 构建类型 + 产品标识 = 构建变种(Build Type + Product Flavor = Build Variant)
正如我们之前所了解的,每个构建类型(Build Type)生成一个新的APK,产品标识(Product Flavor)也是一样:项目的输出变成了构建类型(Build Type)与产品标识(Product Flavor)(如果存在)的所有可能组合。每个组合(构建类型(Build Type)、产品标识(Product Flavor))称之为构建变种(Build Variant)。例如,与默认的debug
和release
构建类型(Build Type)组合,上面例子生成的构建变种(Build Variant)为:
- Flavor1 - debug
- Flavor1 - release
- Flavor2 - debug
- Flavor2 - release
没有产品标识(Product Flavor)的项目也是有构建变种(Build Variant)的,但使用的是默认的标识(flavor)/配置,是没有名字的,使得变种(variant)列表与构建类型(Build Type)列表相同。
5.3 产品标识配置(Product Flavor Configuration)
每个标识(flavor)使用闭包来配置:
android {
...
defaultConfig {
minSdkVersion 8
versionCode 10
}
productFlavors {
flavor1 {
packageName "com.example.flavor1"
versionCode 20
}
flavor2 {
packageName "com.example.flavor2"
minSdkVersion 14
}
}
}
注意android.productFlavors.*
中的对象是ProductFlavor
类型,和android.defaultConfig
对象类型一致。这意味着他们使用相同的属性。
defaultConfig
为所有标识(flavor)提供基本配置,每个标识(flavor)可以重新配置这些值。在上面的例子中,最终的配置为:
-
flavor1
- packageName:com.example.flavor1
- minSdkVersion:8
- versionCode:20
-
flavor2
- packageName:com.example.flavor2
- minSdkVersion:14
- versionCode:20
通常情况下,构建类型(Build Type)会覆盖其他的配置。例如,构建类型(Build Type)的packageNameSuffix
会添加到产品标识(Product Flavor)的packageName
后缀。同时也存在一个配置可以在构建类型(Build Type)和产品标识(Product Flavor)上同时配置的一些情况。在这种情况下,根据具体情况进行配置。例如,signingConfig
是这些属性的一个示例。我们可以在android.buildTypes.release.signingConfig
上配置同样的SigningConfig到所有发布包,也可以分别通过设置android.productFlavors.*.signingConfig
来使用自己的SigningConfig到发布包。
5.4 源码集合和依赖关系(Sourcesets and Dependencies)
与构建类型(Build Type)类似,产品标识(Product Flavor)也可以通过自己的源码集合提供代码和资源。上面的例子创建了4个源码集合:
- android.sourceSets.flavor1 Location src/flavor1/
- android.sourceSets.flavor2 Location src/flavor2/
- android.sourceSets.androidTestFlavor1 Location src/androidTestFlavor1/
- android.sourceSets.androidTestFlavor2 Location src/androidTestFlavor2/
这些源码集合与android.sourceSets.main
和构建类型(Build Type)源码集合一起用来构建APK。当构建一个APK时,使用以下规则来处理所有源码集合:
- 所有源代码(src/*/java)作为多目录结构合并生成一个输出。
- 所有清单(manifest)合并为一个清单(manifest)。与构建类型(Build Type)类似,同样允许产品标识(Product Flavor)有不同的组件或权限。
- 所有的资源(res和assets)的优先级为构建类型(Build Type)覆盖产品标识(Product Flavor),覆盖
main
源码集合。 - 每个构建变种(Build Variant)从自己的资源中生成自己的R类,构建变种(Build Variant)之间不共享任何东西。
最后,与构建类型(Build Type)一样,产品标识(Product Flavor)可以有自己的依赖关系。例如,如果标识(flavor)用于生成一个广告版本应用和付费版本应用,其中广告版本可以依赖于广告SDK,而另一个则不依赖:
dependencies {
flavor1Compile "..."
}
在这示例中,src/flavor1/AndroidManifest.xml
文件可能需要包含访问网络权限。
每个变种(variant)也会创建额外的源码集合:
- android.sourceSet.flavor1Debug Location src/flavor1Debug/
- android.sourceSet.flavor1Release Location src/flavor1Release/
- android.sourceSet.flavor2Debug Location src/flavor2Debug/
- android.sourceSet.flavor2Release Location src/flavor2Release/
这些变种(variant)比构建类型(Build Type)的源码集合有更高的优先级,也允许自定义定制。
5.5 构建和任务(Building and Tasks)
我们之前了解到了每个构建类型(Build Type)都创建自己的assemble\
当使用产品标识(Product Flavor)时,将创建更多的assemble类型任务,分别是:
- assemble\
- assemble\
- assemble\
#1 允许直接构建一个变种(variant)。例如assembleFlavor1Debug
#2 允许构建指定构建类型(Build Type)的所有APK。例如assembleDebug
任务构建Flavor1Debug
和Flavor2Debug
两个变种(variant)
#3 允许构建指定的产品标识(Product Flavor)的所有APK。例如assembleFlavor1
任务构建Flavor1Debug
和Flavor1Release
两个变种(variant)
assemble
任务会构建所有可能的变种(variant)。
5.6 多标识变种(Multi-flavor variants)
在一些情况下,一个应用可能需要基于多个标准创建不同版本。例如,Google Play中的multi-apk支持提供4种不同的过滤器。可以通过多维的产品标识(Product Flavor)来创建不同的APK,用于区分不同的过滤需求。
我们可以考虑一个需要示例版本和支付版本,以及需要在multi-apk支持中使用ABI过滤器的游戏例子。由于存在3种ABI以及2个版本,所以需要生成6个APK(这里并未包含不同的构建类型(Build Type))。然而,支付版本的源代码在3种ABI中都是一致的,所以不能简单地创建6个标识(flavor)。相反,这里需要使用二维标识(flavor),然后变种(variant)会自动构建所有可能的组合。
该特性通过标识维度(Flavor Dimensions)实现。标识(flavor)需要分配一个具体维度:
android {
...
flavorDimensions "abi", "version"
productFlavors {
freeapp {
dimension "version"
...
}
paidapp {
dimension "version"
...
}
arm {
dimension "abi"
...
}
mips {
dimension "abi"
...
}
x86 {
dimension "abi"
...
}
}
}
android.flavorDimensions
数组定义了可能的维度及其顺序。每个已定义的产品标识(Product Flavor)分配到一个维度。
根据以上代码生成的产品标识(Product Flavor)维度[freeapp,paidapp]和[x86,arm,mip],以及构建类型(Build Type)[debug,release],生成以下变种(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.flavorDimensions
定义的维度顺序是很重要的。
每个变种(variant)通过多个产品标识(Product Flavor)来配置:
- android.defaultConfig
- ABI维度其中之一
- 版本维度其中之一
维度的顺序决定了哪个标识(flavor)覆盖哪个标识(flavor),这对于标识(flavor)中的值会替换掉在低优先级标识(flavor)中定义的值的资源而言是非常重要的。
标识(flavor)维度越早定义则有更高的优先级,所以在该实例中:
abi > version > defaultConfig
多标识(multi-flavor)项目也有额外的源码集合,与变种(variant)源码集合相似,但不包含构建类型(Build Type):
- android.sourceSets.x86Freeapp Location src/x86Freeapp/
- android.sourceSet.armPaidapp Location src/armPaidapp/
- 等等...
这提供了在标识组合(flavor-combination)层面上的定制。标识组合(flavor-combination)源码集合的优先级高于基本的标识(flavor)源码集合,但低于构建类型(Build Type)源码集合。
5.7 测试(Testing)
测试多标识(multi-flavor)项目类似于简单标识(flavor)项目。
androidTest
源码集合用于所有标识(flavor)的常用测试,同时每个标识(flavor)有自己的测试源码集合。
如上所述,每个标识(flavor)的测试源码集合会被创建:
- android.sourceSets.androidTestFlavor1 Location src/androidTestFlavor1/
- android.sourceSets.androidTestFlavor2 Location src/androidTestFlavor2/
同样的,它们也有自己的依赖关系:
dependencies {
androidTestFlavor1Compile "..."
}
运行测试可以通过deviceCheck
锚任务,或者当标识(flavor)被使用时充当锚任务的androidTest
任务。
每个标识(flavor)都有自己的任务来运行测试:androidTest\
- androidTestFlavor1Debug
- androidTestFlavor2Debug
同样的,测试APK的构建任务和安装/卸载任务是相对于每个变种(variant):
- assembleFlavor1Test
- installFlavor1Debug
- installFlavor1Test
- uninstallFlavor1Debug
- ...
最终的HTML报告生成支持根据标识(flavor)分类。
测试结果和报告的位置如下,先是每个标识(flavor)版本,然后是所有版本集合:
- build/androidTest-results/flavors/
- build/androidTest-results/all/
- build/reports/androidTests/flavors
- build/reports/androidTests/all/
自定义路径只会变更根目录,始终会创建每个标识(flavor)版本以及所有版本集合的报告和结果的子目录。
5.8 BuildConfig
当编译时,Android Studio生成一个BuildConfig
类,其中包含了构建一个特别变种(variant)时使用的常量值。你可以通过检查这些常量值来调整不同变种(variant)间的表现,例如:
private void javaCode() {
if (BuildConfig.FLAVOR.equals("paidapp")) {
doIt();
} else {
showOnlyInPaidAppDialog();
}
}
以下是BuildConfig
中包含的常量值:
- boolean DEBUG - 构建是否测试版本
- int VERSION_CODE
- String VERSION_NAME
- String APPLICATION_ID
- String BUILD_TYPE - 构建类型(Build Type)名称,如“release”
- String FLAVOR - 标识(flavor)名称,如“paidapp”
如果项目使用多维标识,会生成额外的值。如以上的例子,会生成以下BuildConfig
:
- String FLAVOR = "armFreeapp"
- String FLAVOR_abi = "arm"
- String FLAVOR_version = "freeapp"
5.9 过滤变种(Filtering Variants)
当你使用维度和标识(flavor)时,你可以以没有意义的变种(variant)结尾。例如你可以定义一个使用你的Web API的标识(flavor),和一个使用硬编码的假数据来测试的标识(flavor)。第二个标识(flavor)只用于开发过程,不需要构建发布版本。你可以使用variantFilter
来删除该变种(variant):
android {
productFlavors {
realData
fakeData
}
variantFilter { variant ->
def names = variant.flavors*.name
if(names.contains("fakeData") && variant.buildType.name == "release") {
variant.ignore = true
}
}
}
使用以上配置,你的项目只有三个变种(variant):
- realDataDebug
- realDataRelease
- fakeDataDebug
查看DSL引用获取variant
中你可以检查的所有属性。