Skip to content

Product Flavors 配置

源:Android 官方文档 - 配置 build 变体

Product Flavors(产品变种)用于创建应用的不同版本,每个版本可以有不同的功能、资源配置和依赖项。通过产品变种,您可以从同一代码库构建多个版本的应用,例如免费版和付费版、不同品牌的定制版本等。

核心概念

产品变种是什么

产品变种代表了应用程序的不同版本,这些版本可以在设备上共存、独立分发到 Google Play,或针对不同市场发布。每个变种可以指定:

  • 不同的 Application ID
  • 不同的功能集
  • 不同的资源(图标、字符串、布局等)
  • 不同的依赖项
  • 不同的最低 SDK 版本

产品变种 vs Build Types

| 维度 | Product Flavors | Build Types | |------|----------------|-------------| | 用途 | 创建应用的不同版本(免费/付费、品牌定制) | 开发生命周期阶段(调试/发布) | | Application ID | 通常不同 | 通常相同(可添加后缀) | | 资源差异 | 功能性差异 | 优化程度差异 | | 典型数量 | 2-5 个 | 2-3 个 | | 示例 | free, paid, demo, full | debug, release, staging |

Build Variants 生成规则

Build Variant = Product Flavor × Build Type

kotlin
// 假设有:
// Product Flavors: demo, full
// Build Types: debug, release

// 将自动生成 4 个 Build Variants:
// 1. demoDebug
// 2. demoRelease
// 3. fullDebug
// 4. fullRelease

配置 Product Flavors

单一维度变种 AGP 3.0+

从 AGP 3.0 开始,所有产品变种必须属于一个命名的变种维度。对于简单场景,只需一个维度即可。

kotlin
android {
    defaultConfig {
        applicationId = "com.example.myapp"
        minSdk = 24
        targetSdk = 36
        versionCode = 1
        versionName = "1.0"
    }
    
    // 声明变种维度(必需)
    flavorDimensions += "version"
    
    productFlavors {
        create("demo") {
            // 分配给 "version" 维度
            dimension = "version"
            
            // 添加 Application ID 后缀
            applicationIdSuffix = ".demo"
            versionNameSuffix = "-demo"
            
            // 可以覆盖 defaultConfig 中的任何属性
            minSdk = 21
        }
        
        create("full") {
            dimension = "version"
            applicationIdSuffix = ".full"
            versionNameSuffix = "-full"
        }
    }
}
groovy
android {
    defaultConfig {
        applicationId 'com.example.myapp'
        minSdk 24
        targetSdk 36
        versionCode 1
        versionName '1.0'
    }
    
    flavorDimensions 'version'
    
    productFlavors {
        demo {
            dimension 'version'
            applicationIdSuffix '.demo'
            versionNameSuffix '-demo'
            minSdk 21
        }
        
        full {
            dimension 'version'
            applicationIdSuffix '.full'
            versionNameSuffix '-full'
        }
    }
}

生成的 Build Variants

  • demoDebugcom.example.myapp.demo (debug)
  • demoReleasecom.example.myapp.demo (release)
  • fullDebugcom.example.myapp.full (debug)
  • fullReleasecom.example.myapp.full (release)

多维度变种

当需要组合多个产品变种的配置时,可以使用多个变种维度。Gradle 会为每个维度的变种和构建类型的所有组合创建构建变体。

kotlin
android {
    defaultConfig {
        applicationId = "com.example.myapp"
        versionCode = 1
        versionName = "1.0"
    }
    
    buildTypes {
        getByName("debug") { }
        getByName("release") { }
    }
    
    // 定义两个变种维度,顺序决定优先级
    // 第一个维度优先级最高
    flavorDimensions += listOf("api", "mode")
    
    productFlavors {
        // "mode" 维度:应用版本类型
        create("demo") {
            dimension = "mode"
            applicationIdSuffix = ".demo"
        }
        
        create("full") {
            dimension = "mode"
            applicationIdSuffix = ".full"
        }
        
        // "api" 维度:支持的 API 级别
        // "api" 维度的配置会覆盖 "mode" 维度的配置
        create("minApi24") {
            dimension = "api"
            minSdk = 24
            versionCode = 30000 + (android.defaultConfig.versionCode ?: 0)
            versionNameSuffix = "-minApi24"
        }
        
        create("minApi23") {
            dimension = "api"
            minSdk = 23
            versionCode = 20000 + (android.defaultConfig.versionCode ?: 0)
            versionNameSuffix = "-minApi23"
        }
        
        create("minApi21") {
            dimension = "api"
            minSdk = 21
            versionCode = 10000 + (android.defaultConfig.versionCode ?: 0)
            versionNameSuffix = "-minApi21"
        }
    }
}
groovy
android {
    defaultConfig {
        applicationId 'com.example.myapp'
        versionCode 1
        versionName '1.0'
    }
    
    buildTypes {
        debug { }
        release { }
    }
    
    flavorDimensions 'api', 'mode'
    
    productFlavors {
        demo {
            dimension 'mode'
            applicationIdSuffix '.demo'
        }
        
        full {
            dimension 'mode'
            applicationIdSuffix '.full'
        }
        
        minApi24 {
            dimension 'api'
            minSdk 24
            versionCode 30000 + android.defaultConfig.versionCode
            versionNameSuffix '-minApi24'
        }
        
        minApi23 {
            dimension 'api'
            minSdk 23
            versionCode 20000 + android.defaultConfig.versionCode
            versionNameSuffix '-minApi23'
        }
        
        minApi21 {
            dimension 'api'
            minSdk 21
            versionCode 10000 + android.defaultConfig.versionCode
            versionNameSuffix '-minApi21'
        }
    }
}

生成的 Build Variants(共 12 个):

命名格式:<api-flavor><mode-flavor><BuildType>

  • minApi24DemoDebugminApi24DemoRelease
  • minApi24FullDebugminApi24FullRelease
  • minApi23DemoDebugminApi23DemoRelease
  • minApi23FullDebugminApi23FullRelease
  • minApi21DemoDebugminApi21DemoRelease
  • minApi21FullDebugminApi21FullRelease

维度优先级

  • flavorDimensions 列表中的排列顺序决定优先级
  • 优先级高的维度:
    • 名称排在前面
    • 配置可覆盖优先级低的维度
    • 源代码集合并时具有更高权重

Product Flavor 常用属性

Application ID 相关

kotlin
productFlavors {
    create("free") {
        // 设置完整的 Application ID
        applicationId = "com.example.myapp.free"
        
        // 或添加后缀(更常用)
        applicationIdSuffix = ".free"
    }
}

详见 模块配置 - Application ID

版本相关

kotlin
productFlavors {
    create("demo") {
        // 覆盖版本号
        versionCode = 1000
        versionName = "1.0-demo"
        
        // 或添加后缀
        versionNameSuffix = "-demo"
    }
}

SDK 版本

kotlin
productFlavors {
    create("legacy") {
        // 支持更低的 API 级别
        minSdk = 21
        targetSdk = 33
    }
    
    create("modern") {
        minSdk = 24
        targetSdk = 36
    }
}

BuildConfig 字段

kotlin
android {
    buildFeatures {
        buildConfig = true
    }
}

productFlavors {
    create("free") {
        buildConfigField("String", "API_KEY", "\"free-api-key-123\"")
        buildConfigField("boolean", "ENABLE_ADS", "true")
        buildConfigField("int", "MAX_USERS", "1")
    }
    
    create("paid") {
        buildConfigField("String", "API_KEY", "\"paid-api-key-456\"")
        buildConfigField("boolean", "ENABLE_ADS", "false")
        buildConfigField("int", "MAX_USERS", "999")
    }
}

在代码中使用:

kotlin
val apiKey = BuildConfig.API_KEY
if (BuildConfig.ENABLE_ADS) {
    showAd()
}

资源值

kotlin
productFlavors {
    create("free") {
        resValue("string", "app_name", "\"MyApp Free\"")
        resValue("string", "api_url", "\"https://free-api.example.com\"")
        resValue("color", "brand_color", "\"#FF0000\"")
    }
    
    create("paid") {
        resValue("string", "app_name", "\"MyApp Pro\"")
        resValue("string", "api_url", "\"https://api.example.com\"")
        resValue("color", "brand_color", "\"#0000FF\"")
    }
}

在 XML 中使用:

xml
<TextView
    android:text="@string/app_name"
    android:textColor="@color/brand_color" />

清单占位符

kotlin
productFlavors {
    create("dev") {
        manifestPlaceholders["hostName"] = "dev.example.com"
        manifestPlaceholders["appIcon"] = "@mipmap/ic_launcher_dev"
    }
    
    create("prod") {
        manifestPlaceholders["hostName"] = "example.com"
        manifestPlaceholders["appIcon"] = "@mipmap/ic_launcher"
    }
}

AndroidManifest.xml 中使用:

xml
<application
    android:icon="${appIcon}">
    <meta-data
        android:name="hostname"
        android:value="${hostName}" />
</application>

签名配置

kotlin
productFlavors {
    create("demo") {
        signingConfig = signingConfigs.getByName("debug")
    }
    
    create("full") {
        signingConfig = signingConfigs.getByName("release")
    }
}

详见 应用签名配置

测试配置

kotlin
productFlavors {
    create("mock") {
        testInstrumentationRunner = "com.example.MockTestRunner"
        testApplicationId = "com.example.myapp.mock.test"
    }
}

多 APK 支持 不推荐,建议使用 AAB

kotlin
productFlavors {
    create("x86") {
        ndk {
            abiFilters += "x86"
        }
        versionCode = 1
    }
    
    create("arm") {
        ndk {
            abiFilters += listOf("armeabi-v7a", "arm64-v8a")
        }
        versionCode = 2
    }
}

过滤构建变体 AGP 4.0+

某些产品变种和构建类型的组合可能没有意义,可以使用 androidComponents 块过滤掉不需要的变体。

kotlin
androidComponents {
    beforeVariants { variantBuilder ->
        // 过滤掉 demo 版本的 minApi21 变体
        if (variantBuilder.productFlavors.containsAll(
                listOf("api" to "minApi21", "mode" to "demo")
            )) {
            variantBuilder.enable = false
        }
        
        // 或者根据 Build Type 过滤
        if (variantBuilder.buildType == "debug" &&
            variantBuilder.productFlavors.contains("mode" to "paid")) {
            variantBuilder.enable = false
        }
    }
}
groovy
androidComponents {
    beforeVariants { variantBuilder ->
        if (variantBuilder.productFlavors.containsAll([api: 'minApi21', mode: 'demo'])) {
            variantBuilder.enabled = false
        }
    }
}

实践案例

案例一:免费版和付费版

kotlin
android {
    flavorDimensions += "tier"
    
    productFlavors {
        create("free") {
            dimension = "tier"
            applicationIdSuffix = ".free"
            versionNameSuffix = "-free"
            
            buildConfigField("boolean", "ENABLE_ADS", "true")
            buildConfigField("boolean", "ENABLE_PREMIUM_FEATURES", "false")
            buildConfigField("int", "MAX_PROJECTS", "3")
            
            resValue("string", "app_name", "\"MyApp Free\"")
        }
        
        create("paid") {
            dimension = "tier"
            applicationIdSuffix = ".pro"
            versionNameSuffix = "-pro"
            
            buildConfigField("boolean", "ENABLE_ADS", "false")
            buildConfigField("boolean", "ENABLE_PREMIUM_FEATURES", "true")
            buildConfigField("int", "MAX_PROJECTS", "999")
            
            resValue("string", "app_name", "\"MyApp Pro\"")
        }
    }
}

dependencies {
    // 仅免费版包含广告 SDK
    "freeImplementation"("com.google.android.gms:play-services-ads:22.6.0")
    
    // 仅付费版包含高级功能库
    "paidImplementation"(project(":premium-features"))
}

案例二:多环境配置

kotlin
android {
    flavorDimensions += "environment"
    
    productFlavors {
        create("dev") {
            dimension = "environment"
            applicationIdSuffix = ".dev"
            versionNameSuffix = "-dev"
            
            buildConfigField("String", "API_BASE_URL", "\"https://dev-api.example.com\"")
            buildConfigField("boolean", "ENABLE_LOGGING", "true")
            buildConfigField("boolean", "ENABLE_CRASH_REPORTING", "false")
            
            manifestPlaceholders["appName"] = "MyApp DEV"
            manifestPlaceholders["appIcon"] = "@mipmap/ic_launcher_dev"
        }
        
        create("staging") {
            dimension = "environment"
            applicationIdSuffix = ".staging"
            versionNameSuffix = "-staging"
            
            buildConfigField("String", "API_BASE_URL", "\"https://staging-api.example.com\"")
            buildConfigField("boolean", "ENABLE_LOGGING", "true"
)
            buildConfigField("boolean", "ENABLE_CRASH_REPORTING", "true")
            
            manifestPlaceholders["appName"] = "MyApp STAGING"
            manifestPlaceholders["appIcon"] = "@mipmap/ic_launcher_staging"
        }
        
        create("production") {
            dimension = "environment"
            
            buildConfigField("String", "API_BASE_URL", "\"https://api.example.com\"")
            buildConfigField("boolean", "ENABLE_LOGGING", "false")
            buildConfigField("boolean", "ENABLE_CRASH_REPORTING", "true")
            
            manifestPlaceholders["appName"] = "MyApp"
            manifestPlaceholders["appIcon"] = "@mipmap/ic_launcher"
        }
    }
}

案例三:白标应用

kotlin
android {
    flavorDimensions += "brand"
    
    productFlavors {
        create("brandA") {
            dimension = "brand"
            applicationId = "com.branda.app"
            
            resValue("string", "app_name", "\"Brand A App\"")
            resValue("color", "brand_primary", "\"#FF6200EE\"")
            resValue("color", "brand_secondary", "\"#FF03DAC5\"")
            
            buildConfigField("String", "BRAND_NAME", "\"Brand A\"")
        }
        
        create("brandB") {
            dimension = "brand"
            applicationId = "com.brandb.app"
            
            resValue("string", "app_name", "\"Brand B App\"")
            resValue("color", "brand_primary", "\"#FFF44336\"")
            resValue("color", "brand_secondary", "\"#FF009688\"")
            
            buildConfigField("String", "BRAND_NAME", "\"Brand B\"")
        }
    }
}

每个品牌使用不同的资源目录:

src/
├── main/          # 共享代码和资源
├── brandA/        # Brand A 特有资源
│   ├── res/
│   │   ├── drawable/
│   │   │   └── logo.png
│   │   └── values/
│   │       └── strings.xml
│   └── java/
└── brandB/        # Brand B 特有资源
    ├── res/
    │   ├── drawable/
    │   │   └── logo.png
    │   └── values/
    │       └── strings.xml
    └── java/

选择和构建变体

在 Android Studio 中选择

  1. 打开 Build > Select Build Variant
  2. Build Variants 面板中选择目标变体
  3. 点击 RunDebug 按钮

命令行构建

bash
# 构建特定变体
./gradlew assembleDemoDebug
./gradlew assembleFullRelease

# 构建所有变体
./gradlew assembleDebug
./gradlew assembleRelease
./gradlew assemble

# 安装特定变体
./gradlew installDemoDebug

常见问题

错误:All flavors must belong to a named flavor dimension AGP 3.0+

原因:从 AGP 3.0 开始,所有产品变种必须分配给一个变种维度。

解决方法

kotlin
android {
    // 添加这行
    flavorDimensions +="version"
    
    productFlavors {
        create("demo") {
            // 分配维度
            dimension = "version"
        }
    }
}

如何为特定变种配置依赖

kotlin
dependencies {
    // 仅用于 demo 变种
    "demoImplementation"("com.example:demo-lib:1.0")
    
    // demo 变种的 debug 构建
    "demoDebugImplementation"("com.example:debug-tools:1.0")
    
    // 仅用于 full 变种的 release 构建
    "fullReleaseImplementation"("com.example:analytics:1.0")
}

详见 依赖管理 - 变体特定依赖

如何在代码中判断当前变种

kotlin
// 使用 BuildConfig
val flavor = BuildConfig.FLAVOR
when (flavor) {
    "demo" -> {
        // Demo 逻辑
    }
    "full" -> {
        // Full 逻辑
    }
}

// 更优雅的方式:使用 BuildConfig 字段
if (BuildConfig.ENABLE_PREMIUM_FEATURES) {
    // 高级功能
}

变种命名最佳实践

  1. 使用小写字母demofullfree(不是 DemoFULL) 2 避免使用保留字:不要使用 testandroidTest
  2. 简短明确freefreeVersion 更好
  3. 一致的命名规则:同一维度的变种使用相似模式

最佳实践

  1. 合理使用维度:大多数应用只需要 1-2 个变种维度
  2. Application ID 管理:为不同变种使用不同的 Application ID,允许多版本共存
  3. 共享代码最大化:将共享代码放在 main/ 源代码集,仅在必要时创建变种特定代码
  4. 版本号策略:为不同变种使用不同的版本号前缀(如 10000、20000)
  5. 过滤无用变体:使用 androidComponents 过滤无意义的组合
  6. 依赖隔离:使用变种特定依赖避免无用库打包
  7. BuildConfig 优于硬编码:使用 buildConfigField 管理环境配置

API 参考