构建变体与多渠道打包:美团/腾讯多渠道打包方案

说到多渠道打包,我脑子里第一个蹦出来的场景就是——几年前我刚接手一个日活百万的App,每次发版光打渠道包就要花掉大半天。那时候团队还在用最原始的方式,一个渠道一个渠道地手动改配置。嗯,后来我彻底重构了这套流程。

今天咱们就来聊聊,Android里构建变体和多渠道打包的那些事儿。说白了,这就是一套「一套代码,到处分发」的工程化方案。

为什么需要多渠道打包?

你想想看,一个App要上架到应用宝、华为、小米、OPPO、vivo、Google Play……每个渠道都想要不同的统计参数、不同的启动图、甚至不同的功能开关。如果每个渠道都维护一个分支,那代码库就炸了。

我个人的习惯是:用一套代码,通过构建变体来区分渠道。这样既能保证核心逻辑统一,又能灵活适配各个渠道的需求。

核心思想:构建变体 = Build Type(构建类型)+ Product Flavor(产品风味)。两者组合,就能生成任意数量的APK变体。

使用productFlavors实现渠道分发

先看一个最基础的配置。在模块的 build.gradle 里,加上 flavorDimensionsproductFlavors

android {
    flavorDimensions "channel"

    productFlavors {
        huawei {
            dimension "channel"
            applicationIdSuffix ".huawei"
            versionNameSuffix "-huawei"
        }
        xiaomi {
            dimension "channel"
            applicationIdSuffix ".xiaomi"
            versionNameSuffix "-xiaomi"
        }
        oppo {
            dimension "channel"
            applicationIdSuffix ".oppo"
            versionNameSuffix "-oppo"
        }
        googlePlay {
            dimension "channel"
            applicationIdSuffix ".google"
            versionNameSuffix "-gp"
        }
    }
}

配置完以后,Gradle会自动生成对应的构建任务。比如 assembleHuaweiDebugassembleXiaomiRelease。你直接双击就能打出对应渠道的包。

小技巧:我习惯在 src/ 目录下按渠道建文件夹,比如 src/huawei/src/xiaomi/。里面放渠道专属的资源文件,比如启动图、渠道配置文件。Gradle构建时会自动合并这些资源。

配置渠道参数

每个渠道通常需要传入一个渠道标识。我见过很多项目用 BuildConfig 来传递:

productFlavors {
    huawei {
        buildConfigField "String", "CHANNEL", "\"huawei\""
    }
    xiaomi {
        buildConfigField "String", "CHANNEL", "\"xiaomi\""
    }
}

然后在代码里直接读 BuildConfig.CHANNEL 就行。不过要注意,BuildConfig 是编译期常量,混淆时不会被移除,性能上没问题。

还有一种更灵活的方式——用 manifestPlaceholders 把渠道参数写到 AndroidManifest.xml 里:

productFlavors {
    huawei {
        manifestPlaceholders = [CHANNEL_VALUE: "huawei"]
    }
    xiaomi {
        manifestPlaceholders = [CHANNEL_VALUE: "xiaomi"]
    }
}

然后在 AndroidManifest.xml 里引用:

<meta-data
    android:name="CHANNEL"
    android:value="${CHANNEL_VALUE}" />

这种方式的好处是,第三方SDK(比如统计SDK)可以直接从 ApplicationInfo 里读取渠道信息,不需要改代码。

注意:如果你用 manifestPlaceholders,记得检查不同渠道的 AndroidManifest.xml 合并规则。我曾经遇到过两个渠道的 meta-data 冲突,导致运行时拿不到正确的渠道值。排查了半天才发现是合并顺序的问题。

美团/腾讯的多渠道打包方案

传统的 productFlavors 方案有个痛点——每增加一个渠道,Gradle就要重新执行一次构建流程。如果渠道数量超过50个,构建时间会变得非常恐怖。

我记得美团早期有上百个渠道,每次发版光打包就要跑一两个小时。后来他们搞了一套「快速打包方案」——只打一个基准包,然后通过修改APK的 META-INF 目录来注入渠道信息。

具体原理是这样的:

  1. 先打一个标准的Release包(不包含任何渠道信息)
  2. 在APK的 META-INF 目录下,创建一个空文件,文件名包含渠道标识(比如 channel_huawei
  3. 重新签名、对齐

这样做的好处是,打包时间从O(n)降到了O(1)。不管你有100个渠道还是1000个渠道,只需要一次构建,剩下的都是文件操作。

腾讯的方案也类似,不过他们用的是 ZipEntry 的注释字段来存储渠道信息。两种方案本质上都是「后处理」的思路——先出基准包,再批量打渠道包。

我的建议:如果你的渠道数量少于20个,直接用 productFlavors 就够用了。配置简单,维护方便。但如果渠道超过50个,一定要上快速打包方案。我在一个项目里从 productFlavors 切换到快速打包后,发版时间从2小时缩短到了10分钟。

构建变体的完整流程

为了让你更直观地理解整个流程,我画了一张图:

构建变体与多渠道打包流程 源码 + 资源 Build Type + Product Flavor Debug / Release × 渠道1 / 渠道2 / ... 方案A:productFlavors 每个渠道独立构建 适合 < 20个渠道 方案B:美团方案 META-INF空文件注入 适合 > 50个渠道 方案C:腾讯方案 ZipEntry注释注入 适合 > 50个渠道 多渠道APK包

一些实战中的坑

最后分享几个我踩过的坑:

  • 签名问题:快速打包方案在修改APK后必须重新签名。我曾经忘了重新签名,结果渠道包安装不上。排查了半天才发现是签名失效了。
  • 资源冲突:不同渠道如果有同名资源文件,Gradle合并时会报错。我的做法是在渠道文件夹里只放差异化的资源,公共资源统一放在 src/main/ 里。
  • 版本号管理:每个渠道的 versionName 最好带上渠道标识,方便线上排查问题。比如 2.3.1-huawei2.3.1-xiaomi
  • 测试覆盖:不要只测主渠道的包。我见过一个项目,主渠道跑得好好的,但某个小众渠道因为 so 库不兼容,直接闪退。所以每个渠道至少要做一次冒烟测试。

一句话总结:渠道少用 productFlavors,渠道多用快速打包。核心是「一套代码,多渠道适配」。别让打包流程成为发版的瓶颈。


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