构建变体:构建类型、产品风味与变体感知依赖

说实话,构建变体这个概念,我第一次接触时也觉得有点绕。但用熟了之后,你会发现它简直是多环境、多渠道发布的利器。今天我们就把它彻底讲透。

构建类型:Debug 与 Release 的底层逻辑

构建类型,说白了就是同一份代码在不同场景下的「打包配置」。最常见的两个就是 Debug 和 Release。

我个人习惯在 build.gradle 里这样配置:

android {
    buildTypes {
        debug {
            // 可调试,不混淆
            debuggable true
            minifyEnabled false
            applicationIdSuffix ".debug"
            versionNameSuffix "-debug"
        }
        release {
            // 生产环境,混淆+压缩
            debuggable false
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}

这里有个细节:applicationIdSuffixversionNameSuffix 是 Debug 构建类型的标配。为什么?因为你可以同时安装 Debug 和 Release 版本到手机上,不会冲突。我在项目中遇到过好几次,测试人员反馈「我装了新版怎么还是老样子」,结果发现装的是 Release 包——嗯,从那以后我强制所有 Debug 包都带后缀。

小技巧:Debug 构建类型默认会启用 crashlytics 的调试模式,但 Release 不会。如果你在 Debug 下看到崩溃日志特别详细,别慌,这是正常的。

产品风味:Flavor 的实战用法

产品风味解决的是「同一套代码,不同渠道/不同定制」的问题。比如你的 App 要上架华为、小米、Google Play,每个渠道的包名、图标、API 地址都不一样。用 Flavor 就对了。

看一个我实际用过的配置:

android {
    flavorDimensions "market", "apiLevel"
    
    productFlavors {
        huawei {
            dimension "market"
            applicationId "com.example.app.huawei"
            manifestPlaceholders = [app_name: "我的应用-华为版"]
        }
        xiaomi {
            dimension "market"
            applicationId "com.example.app.xiaomi"
            manifestPlaceholders = [app_name: "我的应用-小米版"]
        }
        google {
            dimension "market"
            applicationId "com.example.app.google"
            manifestPlaceholders = [app_name: "My App"]
        }
        
        minApi21 {
            dimension "apiLevel"
            minSdkVersion 21
        }
        minApi26 {
            dimension "apiLevel"
            minSdkVersion 26
        }
    }
}

你想想看,这样组合出来有多少种变体?3 个市场 × 2 个 API 等级 = 6 种。Gradle 会自动生成对应的任务,比如 assembleHuaweiMinApi21Debug

核心要点:Flavor 的维度(dimension)一定要先定义好。维度顺序决定了变体名称的生成规则。我个人习惯把「变化最频繁的维度」放在前面,这样打包时更容易识别。

变体过滤:只构建你需要的

项目大了之后,全量构建所有变体会非常慢。我记得有一次,项目有 4 个维度、每个维度 3 个选项,总共 81 种变体。每次 assemble 都要跑十几分钟。后来我用了变体过滤,只保留需要的几种。

过滤方式有两种:

  1. build.gradle 中配置过滤规则
  2. 在命令行中指定变体名称

第一种方式:

android {
    variantFilter { variant ->
        def names = variant.flavors*.name
        // 如果市场是 google 且 API 等级是 minApi21,就跳过
        if (names.contains("google") && names.contains("minApi21")) {
            setIgnore(true)
        }
        // 只保留 Debug 和 Release,跳过其他构建类型
        if (variant.buildType.name != "debug" && variant.buildType.name != "release") {
            setIgnore(true)
        }
    }
}

第二种方式更直接:

./gradlew assembleHuaweiMinApi26Release

为什么推荐第二种?因为 CI/CD 环境里,你只需要构建一个特定的变体,没必要在 Gradle 文件里写死过滤逻辑。我曾经踩过一个坑:在 variantFilter 里过滤掉了某些变体,结果 QA 说「为什么这个渠道的包打不出来了?」——嗯,从那以后我尽量不在代码里写死过滤,而是交给 CI 脚本控制。

注意:变体过滤会影响依赖解析。如果你过滤掉了某个变体,但其他变体依赖了它,Gradle 会报错。所以过滤前先理清依赖关系。

变体感知依赖:Gradle 的智能匹配

这是 Gradle 最强大的特性之一。说白了,就是 Gradle 会根据你当前构建的变体,自动选择对应的依赖版本。比如 Debug 下用测试库,Release 下用生产库。

配置方式很简单:

dependencies {
    // 所有变体都依赖
    implementation 'com.squareup.okhttp3:okhttp:4.9.0'
    
    // 仅 Debug 变体依赖
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
    
    // 仅 Release 变体依赖
    releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:2.7'
    
    // 特定 Flavor 变体依赖
    huaweiImplementation 'com.huawei.hms:push:5.3.0.301'
    xiaomiImplementation 'com.xiaomi.mipush:sdk:4.2.0'
}

你想想看,这有多方便?Debug 下自动引入 LeakCanary 检测内存泄漏,Release 下自动换成空实现,完全不用手动切换。我在项目中遇到过最典型的场景是:Debug 下用 stetho 调试数据库,Release 下去掉——一行配置搞定。

变体感知依赖的匹配规则是这样的:

依赖声明 匹配的变体
implementation 所有变体
debugImplementation 所有 Debug 构建类型
releaseImplementation 所有 Release 构建类型
huaweiImplementation 所有包含 huawei Flavor 的变体
huaweiDebugImplementation 仅 huawei + Debug 组合

这里有个容易踩的坑:如果你同时声明了 huaweiImplementationdebugImplementation,Gradle 会优先匹配更具体的那个。比如 huaweiDebugImplementation 的优先级最高。我曾经因为没搞清楚这个优先级,导致 Debug 下用了 Release 的依赖,排查了半天才发现是声明顺序的问题。

避坑指南:变体感知依赖的命名规则是 <flavorName><buildType>Implementation。注意大小写:Flavor 首字母大写,BuildType 首字母小写。比如 huaweiDebugImplementation 是正确的,HuaweiDebugImplementation 会报错。

知识体系总览

下面这张图是我自己总结的构建变体核心逻辑,你可以对照着理解:

构建变体核心逻辑 构建类型 Debug / Release 可调试性、混淆、签名 产品风味 Flavor + 维度 渠道定制、API等级 变体过滤 variantFilter 按需构建,提升效率 构建变体 = 构建类型 × 产品风味 例如:HuaweiMinApi26Debug 变体感知依赖 debugImplementation / huaweiImplementation Gradle 自动匹配当前变体的依赖

从这张图你可以看到,构建类型和产品风味是「输入」,变体过滤是「控制开关」,变体感知依赖是「输出结果」。整个流程是串起来的,缺一不可。

最后说一句:构建变体这个功能,用好了能极大提升开发效率。但别一开始就搞太复杂,从 2-3 个变体开始,慢慢扩展。我见过有人一上来就搞 20 个变体,结果 CI 跑一次要 40 分钟——嗯,那就不叫效率了,叫灾难。


公众号:蓝海资料掘金营,微信deep3321