多渠道打包是Android发布流程里绕不开的一环。运营需要统计各个应用市场的下载量和转化数据,这些数据都依赖APK内写入的渠道号。最原始的做法是用gradle的productFlavors为每个渠道单独编译一次,渠道少的时候还能接受,一旦渠道数量到几十个,打包时间会成倍增长,因为每个渠道包都要完整走一遍编译、dex、签名流程。美团开源的Walle和腾讯开源的VasDolly都是针对这个痛点的解决方案,核心思路是利用Android 7.0引入的Signature V2签名方案,在APK签名块中插入渠道信息,从而实现一次编译、快速生成所有渠道包。本文将完整讲解这两款工具的配置和使用。

一、原理简介:为什么V2签名能实现秒级打包
Android 7.0之前,APK使用V1签名,也就是JAR签名,它基于ZIP文件结构对每个文件做校验。V2签名则是对整个APK做校验,签名信息保存在ZIP Entry之间的一个特殊区域,官方称之为APK Signing Block。关键在于,系统校验V2签名时只关注APK Signing Block中的特定字段,往这个块的额外区域写入自定义数据,不会破坏签名的有效性。
VasDolly和Walle正是利用这个特性,把渠道字符串写入APK Signing Block中。生成渠道包时,只需要复制一份基础APK,然后修改签名块里的渠道信息即可,完全不需要重新编译。一个100M的APK生成几十个渠道包,通常只需要几秒钟到几十秒钟,效率提升是数量级的。
需要注意的是,VasDolly除了V2方案外,还支持基于V1签名的方案,它会在ZIP文件comment区域写入渠道信息,兼容那些 minSdkVersion 低于24的设备。这一点是VasDolly相比Walle的一个优势。
二、Walle的接入与配置
Walle的使用分为两步:先在宿主工程接入读取渠道的SDK,再用命令行工具生成渠道包。首先在根目录的build.gradle中添加插件依赖:
buildscript {
dependencies {
classpath 'com.meituan.android.walle:payload_gradle_plugin:1.1.7'
}
}
然后在app模块的build.gradle中apply插件,并添加channel配置块:
apply plugin: 'walle'
android {
// ... 常规配置
}
walle {
// 指定渠道文件,每行一个渠道
channelFile = new File("${project.projectDir}/channel")
// 指定输出目录
apkOutputFolder = new File("${project.buildDir}/outputs/apk")
// 渠道包文件名模板
apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}.apk'
// configField可自定义额外的键值对写入APK
configField('key1', 'value1')
}
读取渠道信息时,在代码中依赖walle的library:
dependencies {
implementation 'com.meituan.android.walle:library:1.1.7'
}
获取渠道的代码非常简单,一行即可:
String channel = WalleChannelReader.getChannel(getApplicationContext()); // 也可以获取自定义的键值对 String value = WalleChannelReader.get(getApplicationContext(), "key1");
打包命令方面,直接在命令行执行gradlew assembleReleaseChannels,插件会先打出基础包,再根据channel文件批量生成渠道包。如果只是拿到了一个已经签名好的APK,也可以下载Walle的CLI工具,用java -jar walle-cli-all.jar batch -c meituan,huawei,xiaomi app-release.apk这样的命令批量写入渠道。
三、VasDolly的接入与配置
VasDolly的接入方式类似,同样需要插件加SDK。在根build.gradle中添加:
classpath 'com.tencent.vasdolly:driver:1.1.1' classpath 'com.tencent.vasdolly:plugin:1.1.1'
app模块中配置:
apply plugin: 'channel'
android {
// ... 常规配置
}
channel {
// 指定多渠道文件
channelFile = file("/Users/yourname/project/channel.txt")
// 输出目录
outputDir = new File("${project.buildDir}/outputs/channels")
// 渠道包文件名模板
apkNameFormat = '${appName}-${channel}-${buildType}-v${versionName}'
// 快速模式:不校验基础包
fastMode = false
// 是否开启V1签名方案(minSdk低于24时需要)
buildChannelApkByV1 = true
}
SDK依赖和读取代码如下:
dependencies {
implementation 'com.tencent.vasdolly:helper:1.1.1'
}
String channel = ChannelReaderUtil.getChannel(getApplicationContext());
命令行执行gradlew channelRelease即可生成所有渠道包。同样地,VasDolly也提供了独立的命令行jar包,支持对已有APK批量写入渠道,其中V2方案的命令是java -jar channel-v2.jar -c channel.txt base.apk -o outputDir,V1方案则是使用channel-v1.jar。如果同时开启V1和V2写入,会执行V1加V2的合并命令。
四、两款工具对比与常见问题
从功能层面看,VasDolly支持V1和V2两种写入方案,对老设备兼容性更好,而且支持渠道与额外信息的组合写入;Walle只支持V2方案,要求minSdkVersion不低于24,或者确保目标设备大多运行Android 7.0以上。从维护状态看,两者近年更新都不算频繁,但原理稳定,生产环境使用没有问题。
接入过程中有几个高频踩坑点需要提醒。第一,基础包必须先经过签名,未签名的APK没有签名块,自然无法写入渠道信息。第二,很多团队发布前会走360加固或者腾讯乐固等加固流程,加固会重签名,导致之前写入的渠道信息丢失,正确的顺序是先加固,再对加固后的APK写入渠道。第三,如果开启V1方案写入,需要注意ZIP comment区域会被占用,某些应用市场的校验工具可能报异常,建议尽量使用V2方案。第四,读取渠道时如果返回null,优先排查基础包是否为release签名包,以及SDK版本与写入工具版本是否匹配。
最后说一下渠道信息的验证方法,两款工具都自带读取命令,例如Walle可以执行java -jar walle-cli-all.jar show app-meituan-release.apk查看APK中写入的渠道和额外信息,发布前用这个命令抽查几个渠道包,可以避免渠道写错导致的统计数据混乱。对于渠道包动辄几十个、上百个的团队来说,把打包流程从小时级压缩到分钟级,这两款工具带来的收益是非常直接的。