Android 多渠道打包
Android 其實并沒有多渠道的概念夸政,所謂的渠道號是人為添加以識別應(yīng)用市場渠道的消恍。
Android Gradle 原生多渠道打包
原理
雖然 Android 本身不支持渠道號,但是 Android-Gradle 插件有維度的概念豺总,通過維度可以實現(xiàn)模擬多渠道打包车伞。
如下面的例子:
flavorDimensions "channel", "version"
productFlavors {
dji {
dimension 'channel'
buildConfigField 'String','channel_key','"dji_sign"'
}
'360' {
dimension 'channel'
buildConfigField 'String','channel_key','"360_sign"'
}
free {
dimension 'version'
}
vip {
dimension 'version'
}
}
通過 productFlavors
與 dimension
的組合關(guān)系,上面的例子可以打出如下的多種組合包:
djifreedebug喻喳、djivipdebug另玖、djifreerelease、djiviprelease表伦、360...
buildConfigField
屬性可以給特殊維度設(shè)置獨有變量日矫,如上述例子中,dji 維度的包會生成一個值為 "dji_sign" 的 channel_key 變量绑榴,而 360 維度的包則會生成一個值為 "360_sign" 的 channel_key 變量哪轿。
buildConfigField
顧名思義是給 BuildConfig 添加不同維度下的變量,與之作用類似的還有 manifestPlaceholders
翔怎,從名字可以看出它是給 AndroidManifest 添加不同維度下的占位符窃诉。關(guān)于這倆個 Gradle 屬性的詳細使用規(guī)則這里不做詳細介紹,各位可以自行 Google赤套。
實現(xiàn)
Module - build.gradle - android 模塊下飘痛,添加如下配置:
flavorDimensions "channel"
productFlavors {
dji {
dimension "channel"
}
baidu {
dimension "channel"
}
xiaomi {
dimension "channel"
}
}
productFlavors.all {
flavor -> manifestPlaceholders.put("channel", name)
}
manifestPlaceholders
創(chuàng)建了一個名為 'channel',值為維度名的占位符容握,然后在 AndroidManifest 配置(引用方式:${占位符}
)該占位符宣脉,以表達渠道的概念:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="channel"
android:value="${channel}" />
</application>
然后控制臺執(zhí)行 ./gradlew build
,就會打出所有維度(渠道)的包剔氏。
用下面的方式獲取 channel 的值上報塑猖。
public String parseChannel() {
String channel = null;
try {
ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPackageName(), GET_META_DATA);
Bundle metaData = appInfo.metaData;
channel = metaData.getString("channel");
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return channel;
}
總結(jié)
原生 Gradle 支持,缺點是工程量大谈跛、速度慢羊苟。
第三方 Gradle 插件多渠道打包
這里推薦使用 packer-ng-plugin
原理
APK 文件就是一個帶簽名信息的 ZIP 文件,根據(jù) ZIP 文件格式規(guī)范感憾,每個 ZIP 文件的最后都必須有一個叫 Central Directory Record 的部分蜡励,這個 CDR 的最后部分叫"end of central directory record",這一部分包含一些元數(shù)據(jù),它的末尾是 ZIP 文件的注釋凉倚。注釋包含 Comment Length 和 File Comment 兩個字段兼都,前者表示注釋內(nèi)容的長度,后者是注釋的內(nèi)容稽寒,正確修改這一部分不會對 ZIP 文件造成破壞扮碧,利用這個字段,我們可以添加一些自定義的數(shù)據(jù)瓦胎,PackerNg 項目就是在這里添加和讀取渠道信息芬萍。
實現(xiàn)
根目錄 build.gradle 添加:
buildscript {
......
dependencies{
// add packer-ng
classpath 'com.mcxiaoke.gradle:packer-ng:1.0.9'
}
}
module 模塊 build.gradle 添加:
apply plugin: 'com.android.application'
apply plugin: 'packer' //1.
android {
...
defaultConfig {
...
}
signingConfigs {
release {
storeFile file("你的簽名地址")
storePassword "你的簽名密碼"
keyAlias "你的簽名別名"
keyPassword "你的簽名密碼"
v2SigningEnabled false //2.一定要把v2簽名關(guān)掉
}
debug {
storeFile file("你的簽名地址")
storePassword "你的簽名密碼"
keyAlias "你的簽名別名"
keyPassword "你的簽名密碼"
v2SigningEnabled false //一定要把v2簽名關(guān)掉
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release //3.
}
debug {
...
signingConfig signingConfigs.debug
}
}
}
dependencies {
...
implementation 'com.mcxiaoke.gradle:packer-helper:1.0.9' //4.
}
點擊同步尤揣,代碼中獲取渠道號:
String market = PackerNg.getMarket(Context);
配置完畢搔啊,開始打包。
打包方式有倆種北戏,這里只介紹控制臺打包负芋,其余參見 Github。
在項目根目錄下創(chuàng)建 markets.txt 文件嗜愈,文件內(nèi)容如下:
google#谷歌渠道
xiaomi#小米渠道
huawei#華為渠道
每行一個渠道旧蛾,#
號表示注釋。
然后控制臺輸入如下命令打包(執(zhí)行命令前建議運行一次項目蠕嫁,確認無誤再執(zhí)行):
./gradlew -Pmarket=markets.txt clean apkRelease
打包成功后锨天,可在 根目錄/build/archives
看到渠道包文件(注意不是 module 下的 build 文件夾)。
總結(jié)
packer-ng-plugin 插件通過修改復(fù)制 zip(apk)實現(xiàn)了快速打包剃毒,缺點是兼容性沒有原生好病袄,可能存在適配問題。
其它多渠道打包方式
除此之外還有其余快速打包方式赘阀,如通過 python 快速修改 apk -- AndroidManifest -- meta_data 屬性益缠,然后重簽名對齊;如通過美團網(wǎng)批量打包工具 walle基公,這里僅提供思路和方向幅慌,具體項目選擇合適的打包方式即可。