一、背景
每种产品变种都代表您可以构建的一个不同的应用版本。例如,您可能希望为应用构建两个版本,一个是内容有限的免费版本,另一个是包含更多内容的付费版本。您还可以根据 API 级别或其他设备变化因素,为应用构建以不同设备为目标的不同版本。
二、配置产品变种
将其添加到构建配置中的 productFlavors 代码块并添加所需的设置。产品变种支持与 defaultConfig 相同的属性,这是因为,defaultConfig 实际上属于 ProductFlavor 类。这意味着,您可以在 defaultConfig 代码块中提供所有变种的基本配置,每个变种均可更改其中任何默认值,如 applicationId。
所有变种都必须属于一个指定的变种维度,即一个产品变种组。您必须将所有变种分配给某个变种维度;否则,您将收到如下所示的构建错误。如果给定的模块仅指定一个变种维度,那么 Android Gradle 插件会自动将该模块的所有变种分配给该维度。
Error:All flavors must now belong to a named flavor dimension.
The flavor 'flavor_name' is not assigned to a flavor dimension.
以下代码示例创建了一个名为“version”的变种维度,并添加了“demo”和“full”产品变种。这些变种提供了它们自己的 applicationIdSuffix 和 versionNameSuffix:
android {
...
defaultConfig {...}
buildTypes {
debug{...}
release{...}
}
// Specifies one flavor dimension.
flavorDimensions "version"
productFlavors {
demo {
// Assigns this product flavor to the "version" flavor dimension.
// If you are using only one dimension, this property is optional,
// and the plugin automatically assigns all the module's flavors to
// that dimension.
dimension "version"
applicationIdSuffix ".demo"
versionNameSuffix "-demo"
}
full {
dimension "version"
applicationIdSuffix ".full"
versionNameSuffix "-full"
}
}
}
创建并配置产品变种后,点击通知栏中的 Sync Now。同步完成后,Gradle 会根据 build 类型和产品变种自动创建 build 变体,并按照 为其命名。例如,如果您创建了“demo”和“full”产品变种,并保留了默认的“debug”和“release”build 类型,则 Gradle 会创建以下 build 变体:
- demoDebug
- demoRelease
- fullDebug
- fullRelease
三、flavor资源合并规则
变体的资源合并功能简直是"神器"一般的存在,可以解决很多业务需求,如不同渠道显示的icon不同,应用名不同等等。Android Studio在对变体目录和main目录进行资源合并时,会遵守这样的规则,假设当前选中的变体是demoDebug:
- 某资源在demo下有,在main中没有,那么在打包时,会将该资源直接合并到main资源中。
- 某资源在demo下有,在main中也有,那么在打包时,会以demo为主,将demo中资源替换掉main中资源。
除了字符串合并外,还有图片(drawable、mipmap)、布局(layout)、清单文件(AndroidManifest.xml)的合并,具体可以自己尝试一下。其中,清单文件的合并需要提醒一点,如果渠道目录下的AndroidManifest.xml与main下的AndroidManifest.xml拥有相同的节点属性,但属性值不同时,那么就需要对main下的AndroidManifest.xml进行修改了,具体修改要根据编译时报错来处理,所以,报错时不要慌,根据错误提示修改就是了。
注意:布局(layout)文件的合并是对整个文件进行替换的~。
四、代码合并
代码文件,顾名思义就是指java目录下的.java文件了,为什么代码叫整合,而资源却是合并呢?因为代码文件是没办法合并的,只能是整合,整合是什么意思?假设当前选中的变体是demoDebug,有一个java文件是Test.java,这个Test.java要么只存在demo/java下,要么只存在于main/java下。
五、flavor多module常见问题
1、在app和它的module中都有同一个维度(比如:flavorDimensions ‘tier’),但你的app有的flavors在module中没有
flavorDimensions 'tier'
productFlavors {
paid {
// 因为依赖app的module在'tier'维度下也有'paid'这个flavor,所以你不用去管,
// gradle会自动为你匹配
dimension 'tier'
}
free {
// 因为module在'tier'维度下没有'free'这个flavor,所以需要指定matchingFallbacks
// 让gradle知道怎么去匹配
// 像下面这样配置,gradle会按顺序依次去匹配module中'tier'维度下的flavor,
// 直到匹配到,否则会报错
matchingFallbacks = ['demo', 'trial']
}
}
注意:对于在同一个维度下,module中有的flavors但app中没有是不会报错的,因为gradle插件根本不会去module中请求flavors。
2、module中有某个dimension维度,但app中没有
// In the app's build.gradle file.
android {
defaultConfig{
// 下面这句话告诉gradle,当遇到一个module中有个app中没有的'minApi'维度时,
// 它应该按照下面这个顺序去匹配这个维度的flavors
missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
// 若其他module中还有更多app中没有的维度,你必须为所有的维度定义回退策略
missingDimensionStrategy 'abi', 'x86', 'arm64'
}
flavorDimensions 'tier'
productFlavors {
free {
dimension 'tier'
// 你可以在一个特定的flavor中覆盖defaultConfig的配置
missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
}
paid { }
}
}
3、排除掉某些不需要的变体
variantFilter { variant ->
def names = variant.flavors*.name
def types = variant.buildType.name
if (names.contains("abi") && types == "debug") {
// Gradle ignores any variants that satisfy the conditions above.
setIgnore(true)
}
}