一、为什么使用美团多渠道打包的方式?
- 打包更加快速
传统的通过productFlavors渠道包的方式,渠道10个以内还可以接受,如果100个渠道包,每个包需要打5Min,就是将近10个小时的打包,而采用美团Walle多渠道打包的方式只需要打一个包的时间。 - 配置更加灵活
可以在APK渠道包中通过配置config文件,针对于不同渠道包配置各个渠道定制化额外信息
二、多渠道打包原理介绍
整个APK(ZIP文件格式)会被分为以下四个区块:
- Contents of ZIP entries(from offset 0 until the start of APK Signing Block)
- APK Signing Block
- ZIP Central Directory
-
ZIP End of Central Directory
这个是V2签名包的APK包格式,新的应用签名方案有着良好的向后兼容性,能完全兼容低于Android 7.0(Nougat)的版本。对比旧签名方案,它有更快的验证速度和更安全的保护。
区块1、3、4都是受保护区块,不允许修改保护区块。美团打包的方式,是在2区块内写入ID-value的扩展信息(渠道信息),并保存到APK中。这样,每打一个渠道包只需复制一个APK,然后在APK中添加一个ID-value即可,这种打包方式速度非常快,对一个30M大小的APK包只需要100多毫秒(包含文件复制时间)就能生成一个渠道包,而在运行时获取渠道信息只需要大约几毫秒的时间。
三、Walle的工程配置及打包方式
在位于项目的根目录 build.gradle 文件中添加Walle Gradle插件的依赖, 如下:
buildscript {
dependencies {
classpath 'com.meituan.android.walle:plugin:1.1.6'
}
}
并在当前App的 build.gradle 文件中apply这个插件,并添加上用于读取渠道号的AAR
apply plugin: 'walle'
dependencies {
compile 'com.meituan.android.walle:library:1.1.6'
}
配置插件
walle {
// 指定渠道包的输出路径
apkOutputFolder = new File("${project.buildDir}/outputs/channels");
// 定制渠道包的APK的文件名称
apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk';
// 渠道配置文件
channelFile = new File("${project.getProjectDir()}/channel")
}
配置项具体解释:
- apkOutputFolder:指定渠道包的输出路径, 默认值为
new File("${project.buildDir}/outputs/apk")
- apkFileNameFormat:定制渠道包的APK的文件名称, 默认值为
${appName}-${buildType}-${channel}.apk
可使用以下变量:
projectName - 项目名字
appName - App模块名字
packageName - applicationId (App包名packageName)
buildType - buildType (release/debug等)
channel - channel名称 (对应渠道打包中的渠道名字)
versionName - versionName (显示用的版本号)
versionCode - versionCode (内部版本号)
buildTime - buildTime (编译构建日期时间)
fileSHA1 - fileSHA1 (最终APK文件的SHA1哈希值)
flavorName - 编译构建 productFlavors 名
插入额外信息
channelFile只支持渠道写入,如果想插入除渠道以外的其他信息,请在walle配置中使用configFile
walle {
// 渠道&额外信息配置文件,与channelFile互斥
configFile = new File("${project.getProjectDir()}/config.json")
}
configFile是包含渠道信息和额外信息的配置文件路径。
配置文件采用json格式,支持为每个channel单独配置额外的写入信息。
{
//extraInfo 不要出现以`channel`为key的情况
/*
不声明extraInfo的channel默认使用的extraInfo
如果没有此项则没有默认extraInfo
*/
"defaultExtraInfo": {
"key2": "20161213",
"key": "20161212"
},
/*
strategy:
1. ifNone (默认适用此策略) : 仅当对应channel没有extraInfo时生效
2. always : 所有channel都生效,channel中extraInfo的key与defaultExtraInfo重复时,覆盖defaultExtraInfo中的内容。
*/
//"defaultExtraInfoStrategy": "always",
"channelInfoList": [
{
"channel": "meituan",
// 此channel将使用自己声明的extraInfo
/*
此alias可以做到写入apk的channel是meituan,而打包时输出的文件名是美团
注意:alias不声明时,walle配置apkFileNameFormat中channel就是channel,否则为alias
*/
"alias": "美团",
"extraInfo": {
"buildtime": "20161212",
"hash": "123"
}
},
{
"channel": "360cn",
// 此channel将使用自己声明的extraInfo
"extraInfo": {
"key": "20161213"
}
},
{
"channel": "googleplay"
// 此channel将使用defaultExtraInfo
},
{
"channel": "xiaomi"
// 此channel将使用defaultExtraInfo
},
{
"channel": "meizu"
// 此channel将使用defaultExtraInfo
},
{
"channel": "wandoujia",
"excludeDefaultExtraInfo": true
//强制声明不使用defaultExtraInfo,默认false
},
{
"channel": "myapp",
"excludeDefaultExtraInfo": true,
//强制声明不使用defaultExtraInfo,默认false
"extraInfo": {
// 尽管exclude default,但也可以继续写入自己的。
"key": "20161212"
}
}
]
}
注意:
- 此配置项与channelFile功能互斥,开发者在使用时选择其一即可,两者都存在时configFile优先执行。
- extraInfo 不要出现以channel为key的情况
如何获取渠道信息:
在需要渠道等信息时可以通过下面代码进行获取
String channel = WalleChannelReader.getChannel(this.getApplicationContext());
生成渠道包
AS右侧点开Gradle工具栏,如下图所示,双击运行Task任务
生成的APK渠道包,根据Walle配置文件路径查找
四、关于友盟统计多渠道的变更
因为友盟统计之前的多渠道统计方式是在AndroidManifast.xml文件中配合Gradle脚本productFlavors实现的多渠道信息集成。采用这种多渠道打包方式之后,productFlavors不存在了,就算没有删除通过原有的获取渠道信息的方式获取到的渠道信息也不对了。所以需要进行变更。很简单,只需在友盟初始化的时候把Channel信息作为参数传入即可。如下所示:
/**
* 作者:郭翰林
* 时间:2018/6/15 0015 12:07
* 注释:Umeng初始化配置
*
* @param mAppContext
* @param mUmengKey
* @param mUmengChannel
* @param mUmengSecret
*/
public UmengModule(Context mAppContext, String mUmengKey, String mUmengChannel, String mUmengSecret) {
this.mUmengKey = mUmengKey;
this.mUmengChannel = mUmengChannel;
this.mUmengSecret = mUmengSecret;
this.mAppContext=mAppContext;
UMConfigure.init(mAppContext, mUmengKey, mUmengChannel, UMConfigure.DEVICE_TYPE_PHONE, mUmengSecret);
}
/**
* 作者:郭翰林
* 时间:2018/6/15 0015 14:15
* 注释:注册友盟,不要做主进程判断和在线程里注册
*/
private void registerUmeng() {
UmengModule module = new UmengModule(sysApplication, "XXXXXXXXXXX",
WalleChannelReader.getChannel(sysApplication), "XXXXXXXXXX");
.........
.........
}