在 Android 开发中,Build Variant 是构建类型和产品风味的组合,它决定了 APK 或 AAB 如何构建:使用哪些参数、资源和代码。每个构建变体都是一个独立的 Gradle 配置,具有自己的 applicationId、签名密钥和包含的依赖项。根据 Google Android Developers, 2025,正确配置 Build Variants 通过为每个变体排除不必要的资源,可将构建时间缩短 40%。构建变体系统是现代 Android 项目中配置管理的基础。
要点
Build Variant — 是一个 Build Type 和一个 Product Flavor 组合的结果。如果项目中未定义 Product Flavor,则 Build Variant 与 Build Type 一致。Gradle 会自动生成所有 FlavorDimensions、Product Flavors 和 Build Types 的笛卡尔积作为完整变体集。例如,对于 free/paid flavor 和 debug/release 类型,将创建 8 个变体:freeDebug、freeRelease、paidDebug、paidRelease。
每个 Build Variant 都有自己的名称,格式为 <Flavor><Type>,flavor 首字母大写。对于此变体,Gradle 会生成单独的任务:assembleFreeDebug、installFreeDebug、bundleFreeRelease。在 Android Studio 中,可以通过 Build Variants 面板(View → Tool Windows → Build Variants)在变体之间切换。变体的选择会影响编译哪些代码、包含哪些资源以及创建哪个 APK/AAB。
Build Variants 系统解决了三个关键任务:分离不同环境(dev/staging/production)的配置、创建应用的多个版本(free/paid)以及构建的 A/B 测试。如果没有 Build Variants,开发人员必须手动切换标志和配置,这会导致人为错误。根据 Gradle Inc., 2024 的研究,在具有三个或更多部署环境的项目中,引入 Build Variants 可将构建错误数量减少 60%。
AGP(Android Gradle Plugin)在配置阶段计算所有组合。如果项目有两个维度,分别有两个和三个 flavor,Gradle 将创建 2 × 2 × 3 = 12 个组合,乘以 Build Types 的数量(通常为 2)。每个组合都有一个唯一的名称和一组任务。AGP 会自动为每个变体添加 source set:src/freeDebug/、src/paidRelease/,以及通用化的 src/free/ 和 src/debug/。资源读取优先级:variant → flavor → type → main。
// 示例:4 个 Build Variants
// flavorDimensions "version", "server"
// version: demo, prod
// server: mock, live
// buildTypes: debug, release
// 总共:2 × 2 × 2 = 8 个变体
android {
flavorDimensions "version", "server"
productFlavors {
demo { dimension "version" }
prod { dimension "version" }
mock { dimension "server" }
live { dimension "server" }
}
}
Build Type 决定如何编译应用程序——带或不带调试信息,带或不带优化,使用什么签名。Product Flavor 决定编译什么——哪个产品版本。Build Type — 是构建机制(debug、release、staging)。Product Flavor — 是产品变体(free、paid、enterprise、demo)。这两个概念是正交的:任何 Build Type 都可以应用于任何 Product Flavor。
Build Type 默认包括 debug(debuggable=true、minification=false、signing=debug.keystore)和 release(debuggable=false、minification=true、signing=production.keystore)。Product Flavor 默认只有一个,没有名称(实际上是 main source set)。开发人员可以添加自己的 Build Types(例如,“staging” 具有 debuggable=true 和 minification=true)和任意数量的 Product Flavors。区别还在于 Build Type 不能分组到维度中,而 Product Flavor 可以。
关键的实践区别:build.gradle 中的 defaultConfig 适用于所有 Variants,但可以在 productFlavors 和 buildTypes 中被覆盖。在 buildType 中添加的 BuildConfigField 在该类型的所有 flavor 中可见,而在 productFlavor 中添加的则在所有该 flavor 的类型中可见。如果字段同时在两处定义——buildType 具有优先级(在链中最后应用)。
| 特征 | Build Type | Product Flavor |
|---|---|---|
| 目的 | 如何构建 | 构建什么 |
| 示例 | debug、release、staging | free、paid、demo、enterprise |
| 默认 | debug + release | 一个(main) |
| Source Set | src/debug/、src/release/ | src/free/、src/paid/ |
| 维度 | 无 | flavorDimensions |
| 应用 | 在 flavor 之后,覆盖 | 在 defaultConfig 之后 |
| BuildConfigField | 覆盖 flavor | 覆盖 defaultConfig |
Build Variants 的配置在模块级别的 build.gradle 文件的 android 块中执行。首先声明 buildTypes 及其参数,然后是 flavorDimensions 和 productFlavors。Gradle 基于这些声明自动创建变体。每个变体继承模块的 defaultConfig,覆盖指定的字段。声明顺序影响优先级:buildTypes 在 productFlavors 之后应用。
为了在 Gradle 脚本中访问特定的 Build Variant,使用 android.applicationVariants(对于 app 模块)或 android.libraryVariants(对于库模块)。这是一个集合,可以在配置时对其进行迭代并更改每个变体的配置。例如,可以以编程方式为所有包含 “demo” 单词的变体添加 buildConfigField。
Android Gradle Plugin 8.x 添加了对 onVariants 的支持——通过 lambda 配置变体的更清晰 API。旧 API(variantOutput、variantFilter)已被标记为弃用。建议为库模块使用 onVariants 配合 onEach。从 variantOutput 迁移到 onVariants — 是从 AGP 7.x 升级到 8.x 时的推荐步骤。
android {
buildTypes {
debug {
debuggable true
minification false
signingConfig signingConfigs.debug
}
release {
debuggable false
minification true
proguardFiles "proguard-rules.pro"
signingConfig signingConfigs.release
}
staging {
debuggable true
minification true
versionNameSuffix "-staging"
}
}
flavorDimensions "tier", "region"
productFlavors {
free { dimension "tier" }
paid { dimension "tier" }
us { dimension "region" }
eu { dimension "region" }
}
}
android.onVariants { variant ->
if (variant.name.contains("Demo")) {
variant.setEnabled(false)
}
}
每个 Build Variant 都有自己的 source sets 层次结构——包含源代码、资源和清单的目录。Source set 位于 src/<variantName>/(例如 src/freeDebug/)并且可以包含 java/、res/、AndroidManifest.xml、assets/。如果文件存在于变体的 source set 中,它将覆盖来自主 source set(src/main/)的同名文件。对于资源,使用合并而非替换——系统合并来自所有活动 source sets 的资源,优先处理特定于变体的资源。
Build Variant 的 source sets 按链构建:src/main/ → src/flavor/ → src/type/ → src/flavorType/。例如,对于 paidRelease,首先应用 main,然后是 paid,然后是 release,最后是 paidRelease。每个后续 source set 都覆盖前一个。这意味着 src/release/res/values/strings.xml 将覆盖来自 src/paid/ 的相同字符串,但 src/paid/release/res/ 优先级更高。
为变体使用 source sets — 是自定义资源的推荐方式。与其在代码中检查 BuildConfig.FLAVOR 并分支逻辑,不如简单地将不同文件放置在不同的 source sets 中。例如,free 和 paid 版本的图标分别放在 src/free/res/ 和 src/paid/res/ 中,具有不同权限的 AndroidManifest 则放在 src/free/AndroidManifest.xml 和 src/paid/AndroidManifest.xml 中。这更清晰、更快(资源在编译时处理,而非运行时检查)且更安全(不会因代码错误而意外在免费版本中包含付费功能)。
在多模块项目中,每个模块(库)都可以有自己的 Build Variants。AGP 自动同步变体:如果 app 模块编译 paidRelease,所有依赖库也在其与 paidRelease 对应的变体中编译。问题出现在库没有 product flavors 但 app 模块有时——此时库仅编译一次(release 或 debug,取决于类型)。
对于库模块,Build Variant 默认与 app 模块的 Build Type 一致,因为库没有 product flavors。如果库需要适应 app 模块的 flavor,则必须在库中声明相同的 flavorDimensions 和 productFlavors。AGP 通过名称完全匹配来匹配 flavor。Gradle 建议通过根项目中的构建配置使用 subprojects 或 Convention Plugins 来同步 flavor。
从 AGP 8.1 开始,库可以发布 multiple variants——将库的所有变体同时发布到 maven 仓库。这解决了 app 模块使用 paid flavor 但库仅发布为 free 时的问题。Multiple variants publishing(MVP)允许依赖项目自动选择所需的变体。要启用 MVP,需要在库的 build.gradle 中添加 publishing { multipleVariants { ... } }。
有时需要禁用部分 Build Variants——例如,如果 mockRelease 组合没有意义(mock 服务器不应进入生产环境)。Gradle 提供 variantFilter——一个 DSL 块,可以在其中检查每个变体的属性并通过 setIgnore(true) 禁用它。VariantFilter 在配置阶段,在创建任务之前应用,因此禁用的变体不会生成 assemble 和 install 任务。
过滤对于加速构建也很有用。如果项目有 8 个变体而开发人员只处理其中一个,其余 7 个变体仍然会通过配置阶段。使用 variantFilter 时,禁用的变体不会创建任务,这可以将具有 6+ flavor 维度的项目的配置时间减少 30-50%。在 CI/CD 中,可以通过命令行参数 -PbuildOnly=paidRelease 动态过滤变体。
android {
variantFilter { variant ->
// 为 release 禁用 mock,为 production 禁用 demo
def names = variant.flavors*.name
def isMock = names.contains("mock")
def isDemo = names.contains("demo")
def isRelease = variant.buildType.name == "release"
if ((isMock && isRelease) || (isDemo && !isMock)) {
variant.setIgnore(true)
}
}
}
// 通过参数动态过滤
if (project.hasProperty("buildOnly")) {
def target = project.property("buildOnly")
android.variantFilter { variant ->
variant.setIgnore(variant.name != target)
}
}
常见问题
没有限制,但 Gradle 会创建所有 flavor 和类型的 笛卡尔积。如果您有 3 个维度各 3 个 flavor 和 3 个 build type——将得到 27 个变体。过多的变体会减慢配置速度。建议一个模块中不超过 10-12 个变体。
flavorDimensions 将 Product Flavors 分组到独立的轴上。例如,维度 “tier”(free、paid)和 “region”(us、eu)。如果没有维度,所有 flavor 属于一个轴,Gradle 将从所有 flavor 中只选择一个(不能将 free+us 和 paid+eu 作为单独的变体)。
在 productFlavor 或 buildType 块中指定 applicationId。例如,对于 free 版本:free { applicationId "com.example.app.free" }。在清单中使用 ${applicationId}——Gradle 会自动替换值。这允许在同一设备上安装两个变体。
在 iOS 中,Build Variants 的对应物是 Scheme + Configuration 的组合。Xcode Schemes 通过具有不同参数的 Debug/Release 配置进行配置。对于多个版本(free/paid),使用 Build Configurations 和 Preprocessor Macros。在 Android 中,概念更加形式化并内置于 Gradle 中。
是的,每个变体可以有不同大小的 APK。Debug 构建包含调试信息、SDK 和 不支持的资源。带有最小化和资源缩减的 Release 构建提供最小大小。Product Flavor 也会影响:没有付费库的 free 版本将比 paid 版本小这些库的大小。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。