4、构建变体(Build Variants):Build Type、Product Flavor、Flavor Dimensions 与变体过滤
构建变体,说白了就是「同一份代码,打出不同版本的包」。
我刚开始接触 Android 构建时,觉得这玩意儿不就是 debug 和 release 嘛,有啥好讲的?后来在一个项目里,我们需要同时维护免费版、付费版、国内版、海外版……嗯,那会儿我才意识到,构建变体才是 Gradle 真正的杀手锏。
4.1 Build Type:debug 与 release 的「性格」差异
Build Type 定义的是「构建模式」。最常见的两个就是 debug 和 release。
debug 包默认会开启可调试、不混淆、签名用默认的 debug keystore。release 包则相反,需要你手动配置签名、开启混淆、压缩资源。
核心区别一览:
| 属性 | debug | release |
|---|---|---|
| debuggable | true | false |
| minifyEnabled | false | true(通常) |
| shrinkResources | false | true(通常) |
| 签名配置 | 默认 debug 签名 | 需手动配置 |
你可以在 build.gradle 里自定义 Build Type:
android {
buildTypes {
debug {
// 默认就是 debug,其实不写也行
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}
我个人习惯在 debug 类型里加个 applicationIdSuffix,这样 debug 和 release 可以同时安装在一台手机上,方便对比测试。
小技巧: 你还可以自定义一个 staging 类型,用于内部测试。比如开启混淆但保留日志输出,这种「半 release 半 debug」的状态,我在项目里经常用。
4.2 Product Flavor:免费版 vs 付费版,不止是价格差异
Product Flavor 解决的是「同一套代码,不同产品线」的问题。比如免费版和付费版,它们的功能、API 地址、甚至应用名都可能不同。
android {
flavorDimensions "version"
productFlavors {
free {
dimension "version"
applicationId "com.example.app.free"
versionNameSuffix "-free"
}
paid {
dimension "version"
applicationId "com.example.app.paid"
versionNameSuffix "-paid"
}
}
}
配置完之后,Gradle 会自动生成对应的 Build Variant:freeDebug、freeRelease、paidDebug、paidRelease。每个变体都可以有自己的 src/free/ 或 src/paid/ 源码目录。
我曾经在一个项目里,免费版和付费版共用了 90% 的代码,只有支付模块和广告模块不同。通过 Product Flavor,我们只需要在各自的源码目录里放不同的实现类,构建时自动切换,非常干净。
注意: 如果两个 Flavor 的 applicationId 不同,它们在 Google Play 上会被视为两个独立的应用。签名、统计、推送 SDK 的配置都要分开处理,别问我怎么知道的……
4.3 Flavor Dimensions:当「免费/付费」遇上「国内/海外」
如果只有一维的 Flavor,那很简单。但现实往往是多维的——比如我们既要区分免费版和付费版,又要区分国内版和海外版。这时候就需要 Flavor Dimensions。
说白了,Dimensions 就是给 Flavor 分组。每个维度代表一个独立的变化轴。
android {
flavorDimensions "version", "market"
productFlavors {
free {
dimension "version"
}
paid {
dimension "version"
}
china {
dimension "market"
}
global {
dimension "market"
}
}
}
这样组合出来的变体就是:freeChinaDebug、freeChinaRelease、freeGlobalDebug、freeGlobalRelease、paidChinaDebug……一共 2×2×2 = 8 个变体。
组合逻辑: Gradle 会按照 flavorDimensions 声明的顺序,将每个维度的 Flavor 做笛卡尔积。第一个维度变化最慢,最后一个维度变化最快。
你想想看,如果维度再多一点,比如再加一个「渠道维度」(华为、小米、应用宝),那变体数量会爆炸。所以,合理规划 Dimensions 非常重要。
4.4 变体过滤与组合:别让无用的变体浪费你的编译时间
变体多了,编译时间就长了。有些组合其实根本用不上,比如「免费版 + 海外版」可能根本不存在。这时候就需要过滤。
Gradle 提供了 variantFilter 来帮你做这件事:
android {
variantFilter { variant ->
def names = variant.flavors*.name
// 如果免费版 + 海外版,直接忽略
if (names.contains("free") && names.contains("global")) {
setIgnore(true)
}
// 如果付费版 + 国内版,也忽略
if (names.contains("paid") && names.contains("china")) {
setIgnore(true)
}
}
}
这样,Gradle 在构建时就不会生成那些无用的变体,编译速度能快不少。
我曾经在一个大项目里,一开始没做过滤,结果变体列表拉下来有 30 多个,每次切换 Build Variant 都要等半天。后来加了过滤,只保留实际需要的 8 个,世界清净了。
建议: 在项目的 build.gradle 里,把过滤逻辑写清楚,最好加个注释说明哪些组合是有效的。不然新同事来了,看到一堆变体,一脸懵逼。
4.5 知识体系总览
下面这张图,帮你把本章的核心逻辑串起来:
从图中可以看到,Build Type 和 Product Flavor 是两条独立的轴,通过 Flavor Dimensions 组合成多维矩阵,再经过过滤,最终输出你真正需要的 APK 或 AAB。
嗯,这一章的内容就到这里。构建变体这块,理解起来不难,但真正用好需要一点经验。我建议你从最简单的 debug/release 开始,慢慢加入 Flavor,等遇到多维度需求时再引入 Dimensions。别一开始就搞得太复杂,否则你可能会被变体列表吓到。