75、多渠道打包:productFlavors、友盟多渠道、美团Walle、V2签名
多渠道打包,说白了就是同一份代码,打出不同渠道的APK。
你想想看,应用商店有华为、小米、应用宝、360……每个渠道都要统计下载量、激活量。总不能每个渠道单独维护一个工程吧?
我刚开始做Android时,就干过这种蠢事——手动改包名、改渠道号,然后一个一个点“Build APK”。那滋味,谁试谁知道。
75.1 为什么需要多渠道打包?
核心需求其实就两个:
- 统计区分:知道用户是从哪个渠道来的
- 差异化配置:不同渠道可能有不同的SDK、不同的启动页
嗯,这里要注意:多渠道 ≠ 多包名。包名通常只有一个,渠道只是APK里的一个标记位。
75.2 方案一:productFlavors(原生方案)
这是Gradle自带的方案,最正统,但也是最慢的。
核心思路:在build.gradle里定义flavor维度,每个flavor可以覆盖资源、代码、依赖。
android {
flavorDimensions "channel"
productFlavors {
huawei {
dimension "channel"
manifestPlaceholders = [CHANNEL_VALUE: "huawei"]
}
xiaomi {
dimension "channel"
manifestPlaceholders = [CHANNEL_VALUE: "xiaomi"]
}
yingyongbao {
dimension "channel"
manifestPlaceholders = [CHANNEL_VALUE: "yingyongbao"]
}
}
}
然后在AndroidManifest.xml里引用占位符:
<meta-data
android:name="CHANNEL"
android:value="${CHANNEL_VALUE}" />
运行时读取:
public static String getChannel(Context context) {
try {
ApplicationInfo ai = context.getPackageManager()
.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
return ai.metaData.getString("CHANNEL");
} catch (Exception e) {
return "unknown";
}
}
我曾经踩过的坑:productFlavors每次打包都会走完整的编译流程。如果你有30个渠道,就要编译30次。我有个项目渠道数到了50个,一次打包要跑一个多小时。那段时间我每天下班前点一下打包,第二天早上来看结果。
75.3 方案二:友盟多渠道(UMeng方案)
友盟的方案其实是对productFlavors的简化封装。它利用Gradle的productFlavors,但把渠道号写在了AndroidManifest的meta-data里。
配置方式类似:
productFlavors {
huawei {}
xiaomi {}
yingyongbao {}
}
productFlavors.all { flavor ->
flavor.manifestPlaceholders = [CHANNEL_VALUE: name]
}
友盟SDK会自动读取这个meta-data,然后上报到友盟后台。
我的建议:如果你只用友盟统计,这个方案够用。但如果你还要接其他SDK(比如广告、推送),每个渠道的配置会变得很复杂。我个人更推荐用Walle。
75.4 方案三:美团Walle(推荐方案)
Walle是美团开源的多渠道打包工具。它的核心思想是:不重新编译,只修改APK的注释块。
为什么快?因为编译一次,生成一个基础APK,然后往APK里写渠道信息。写一个渠道只需要几十毫秒。
原理:APK文件其实是一个ZIP包。ZIP文件末尾有一个Comment区域(最大65535字节)。Walle把渠道信息写在这个Comment里,不破坏签名。
接入方式:
// 项目根build.gradle
buildscript {
dependencies {
classpath 'com.meituan.android.walle:plugin:1.1.7'
}
}
// app/build.gradle
apply plugin: 'walle'
walle {
apkOutputPath = "${project.buildDir}/outputs/channels"
channelFile = file("channel.txt")
}
channel.txt内容:
huawei
xiaomi
yingyongbao
360
oppo
vivo
打包命令:
./gradlew clean assembleReleaseChannels
读取渠道:
String channel = WalleChannelReader.getChannel(context);
我个人的经验:Walle是我目前最常用的方案。50个渠道,编译一次+写入50次,总共不到5分钟。相比productFlavors的一个多小时,简直是天壤之别。
75.5 V2签名与Walle的兼容性
Android 7.0(API 24)引入了V2签名方案。V2签名把签名信息放在了APK的中央区块,而不是ZIP末尾。
这就带来一个问题:Walle往ZIP末尾写渠道信息,会不会破坏V2签名?
答案是:不会。Walle专门做了兼容处理。它把渠道信息写在V2签名的“签名块”之后、ZIP中央目录之前。这个位置是V2签名规范允许的扩展区域。
我曾经踩过的坑:如果你同时使用了V1和V2签名,Walle写入后V1签名会失效。但没关系,Android 7.0以上只校验V2签名。如果你要兼容Android 6.0以下,记得保留V1签名。Walle默认会保留V1签名块,但写入后V1签名会变成“未签名”状态。解决办法是:打包时只使用V2签名,或者用Walle的“增强版”模式。
配置V2签名:
android {
signingConfigs {
release {
v1SigningEnabled true
v2SigningEnabled true
}
}
}
75.6 三种方案对比
| 方案 | 打包速度 | 灵活性 | 签名兼容 | 推荐场景 |
|---|---|---|---|---|
| productFlavors | 慢(全量编译) | 高(可覆盖代码/资源) | 完全兼容 | 渠道间差异大(如不同SDK) |
| 友盟多渠道 | 慢(全量编译) | 中(仅渠道号) | 完全兼容 | 仅需友盟统计 |
| 美团Walle | 极快(秒级写入) | 低(仅渠道号) | 需注意V2签名 | 渠道多、差异小 |
75.7 我的实战建议
- 渠道数少于10个:用productFlavors就行,简单直接
- 渠道数10~50个:用Walle,省时间
- 渠道数超过50个:必须用Walle,否则打包时间会让你崩溃
- 渠道间有代码差异:用productFlavors + Walle混合。先用productFlavors打出几个“变体”APK,再用Walle写入具体渠道号
一个小技巧:我习惯在CI/CD流水线里用Walle。编译一次基础APK,然后并行写入所有渠道。整个流程控制在3分钟以内。以前用productFlavors,CI跑一次要40分钟,开发同学经常抱怨“等打包等到下班”。
75.8 知识体系图
嗯,以上就是多渠道打包的三种主流方案。我个人最常用Walle,但也不排斥productFlavors——看场景。你想想看,如果每个渠道都要换不同的启动页、不同的广告SDK,那还是老老实实用productFlavors吧。
公众号:蓝海资料掘金营,微信deep3321