一、為什么使用美團(tuán)多渠道打包的方式庄涡?
- 打包更加快速
傳統(tǒng)的通過productFlavors渠道包的方式量承,渠道10個以內(nèi)還可以接受,如果100個渠道包穴店,每個包需要打5Min,就是將近10個小時的打包撕捍,而采用美團(tuán)Walle多渠道打包的方式只需要打一個包的時間。 - 配置更加靈活
可以在APK渠道包中通過配置config文件泣洞,針對于不同渠道包配置各個渠道定制化額外信息
二忧风、多渠道打包原理介紹
整個APK(ZIP文件格式)會被分為以下四個區(qū)塊:
- 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包格式,新的應(yīng)用簽名方案有著良好的向后兼容性球凰,能完全兼容低于Android 7.0(Nougat)的版本狮腿。對比舊簽名方案,它有更快的驗證速度和更安全的保護(hù)呕诉。
區(qū)塊1缘厢、3、4都是受保護(hù)區(qū)塊甩挫,不允許修改保護(hù)區(qū)塊贴硫。美團(tuán)打包的方式,是在2區(qū)塊內(nèi)寫入ID-value的擴(kuò)展信息(渠道信息),并保存到APK中英遭。這樣间护,每打一個渠道包只需復(fù)制一個APK,然后在APK中添加一個ID-value即可挖诸,這種打包方式速度非持撸快,對一個30M大小的APK包只需要100多毫秒(包含文件復(fù)制時間)就能生成一個渠道包多律,而在運行時獲取渠道信息只需要大約幾毫秒的時間痴突。
三、Walle的工程配置及打包方式
在位于項目的根目錄 build.gradle 文件中添加Walle Gradle插件的依賴狼荞, 如下:
buildscript {
dependencies {
classpath 'com.meituan.android.walle:plugin:1.1.6'
}
}
并在當(dāng)前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:指定渠道包的輸出路徑, 默認(rèn)值為
new File("${project.buildDir}/outputs/apk")
- apkFileNameFormat:定制渠道包的APK的文件名稱, 默認(rèn)值為
${appName}-${buildType}-${channel}.apk
可使用以下變量:
projectName - 項目名字
appName - App模塊名字
packageName - applicationId (App包名packageName)
buildType - buildType (release/debug等)
channel - channel名稱 (對應(yīng)渠道打包中的渠道名字)
versionName - versionName (顯示用的版本號)
versionCode - versionCode (內(nèi)部版本號)
buildTime - buildTime (編譯構(gòu)建日期時間)
fileSHA1 - fileSHA1 (最終APK文件的SHA1哈希值)
flavorName - 編譯構(gòu)建 productFlavors 名
插入額外信息
channelFile只支持渠道寫入粘秆,如果想插入除渠道以外的其他信息,請在walle配置中使用configFile
walle {
// 渠道&額外信息配置文件收毫,與channelFile互斥
configFile = new File("${project.getProjectDir()}/config.json")
}
configFile是包含渠道信息和額外信息的配置文件路徑攻走。
配置文件采用json格式,支持為每個channel單獨配置額外的寫入信息此再。
{
//extraInfo 不要出現(xiàn)以`channel`為key的情況
/*
不聲明extraInfo的channel默認(rèn)使用的extraInfo
如果沒有此項則沒有默認(rèn)extraInfo
*/
"defaultExtraInfo": {
"key2": "20161213",
"key": "20161212"
},
/*
strategy:
1. ifNone (默認(rèn)適用此策略) : 僅當(dāng)對應(yīng)channel沒有extraInfo時生效
2. always : 所有channel都生效昔搂,channel中extraInfo的key與defaultExtraInfo重復(fù)時,覆蓋defaultExtraInfo中的內(nèi)容输拇。
*/
//"defaultExtraInfoStrategy": "always",
"channelInfoList": [
{
"channel": "meituan",
// 此channel將使用自己聲明的extraInfo
/*
此alias可以做到寫入apk的channel是meituan摘符,而打包時輸出的文件名是美團(tuán)
注意:alias不聲明時,walle配置apkFileNameFormat中channel就是channel策吠,否則為alias
*/
"alias": "美團(tuán)",
"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逛裤,默認(rèn)false
},
{
"channel": "myapp",
"excludeDefaultExtraInfo": true,
//強制聲明不使用defaultExtraInfo,默認(rèn)false
"extraInfo": {
// 盡管exclude default猴抹,但也可以繼續(xù)寫入自己的带族。
"key": "20161212"
}
}
]
}
注意:
- 此配置項與channelFile功能互斥,開發(fā)者在使用時選擇其一即可蟀给,兩者都存在時configFile優(yōu)先執(zhí)行蝙砌。
- extraInfo 不要出現(xiàn)以channel為key的情況
如何獲取渠道信息:
在需要渠道等信息時可以通過下面代碼進(jìn)行獲取
String channel = WalleChannelReader.getChannel(this.getApplicationContext());
生成渠道包
AS右側(cè)點開Gradle工具欄,如下圖所示跋理,雙擊運行Task任務(wù)
生成的APK渠道包择克,根據(jù)Walle配置文件路徑查找
四、關(guān)于友盟統(tǒng)計多渠道的變更
因為友盟統(tǒng)計之前的多渠道統(tǒng)計方式是在AndroidManifast.xml文件中配合Gradle腳本productFlavors實現(xiàn)的多渠道信息集成前普。采用這種多渠道打包方式之后肚邢,productFlavors不存在了,就算沒有刪除通過原有的獲取渠道信息的方式獲取到的渠道信息也不對了汁政。所以需要進(jìn)行變更道偷。很簡單缀旁,只需在友盟初始化的時候把Channel信息作為參數(shù)傳入即可。如下所示:
/**
* 作者:郭翰林
* 時間: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
* 注釋:注冊友盟勺鸦,不要做主進(jìn)程判斷和在線程里注冊
*/
private void registerUmeng() {
UmengModule module = new UmengModule(sysApplication, "XXXXXXXXXXX",
WalleChannelReader.getChannel(sysApplication), "XXXXXXXXXX");
.........
.........
}