Android国内海外apk多渠道打包了解一下?
本文主要讲解一个代码工程需要同时需要打包国内外apk包,国外一般只在google play发布,国内发布的平台会更多一些,包括华为、小米、应用宝、vivo等应用市场平台
需求整理
国内和海外版本差异一般比较大,有一部分代码都是不一样的,比如国外一般使用google推送,而国内一般使用个推等第三方推送平台。国内的版本,功能都是一致,一般只是渠道号不一致,以保证数据统计需求,而其它功能都是一致的,那么主要需要解决以下几个问题:
- 国内版本支持一次打包多个渠道不一样的apk,它们之间的代码逻辑是一样的
- 一次工程,支持打外国内、海外两类apk,它们之间的代码逻辑是不一样的
- 国内和海个任一apk间需要区分渠道号
- 实现一个自动化打包的脚本,批量生成apk版本、混淆规则的mapping文件
实现效果
打包输出结果
以下是打包的输出文件,可直接用于输出提测,文件结构如下:
├── cn
│ ├── app_2.7.0.0_2070000_test_release_20200916-1636-mapping.txt
│ └── apks
│ ├── app-v2.7.0.0-c360.apk
│ ├── app-v2.7.0.0-hw.apk
│ ├── app-v2.7.0.0-lggCn.apk
│ ├── app-v2.7.0.0-oppo.apk
│ ├── app-v2.7.0.0-vivo.apk
│ ├── app-v2.7.0.0-xm.apk
│ └── app-v2.7.0.0-yyb.apk
└── gp
├── app_2.7.0.1_2070001_test_online_20200916-1649-mapping.txt
└── apks
└── app_2.7.0.1_2070001_test_online_20200916-1649.apk
实现步骤
如何实现代码多渠道
实现代码多渠道依赖通过在build.gradle中配置productFlavors属性,以下定义了两个渠道,分别为cn和gp
android {
...
productFlavors {
//定义国内渠道
cn {}
//定义google play渠道
gp {}
}
}
定义在两个渠道后可以在cn和gp两个渠道中编写差异化的代码,效果如下:
定义在cn和gp对应的同包名类名下定义相关的类并实现
class PushManager {
//不同的渠道实现不同的代码
fun init() {
...
}
在调试代码时,可设置当前的生效渠道并运行,打包后面会提到
海外apk打包
海外渠道定义为gp,且编译类型分为debug、release、online
那么在打海外线上包就可执行以下命令
gradlew assembleGpOnline
但是这样打包的输出文件其实在build目录了,我们就需要实现以下几个点:
- 在apk命名加入版本号、日期等信息
- 将apk文件拷贝到指定输出目录,类似“…/app_build_output”
- 拷贝混淆规则文件
具体的实现如下
applicationVariants.all { variant ->
variant.outputs.all { output ->
if (variant.buildType.name != "debug") {
variant.assembleProvider.get().doLast {
def outputFile = output.outputFile
def targetDir = "$rootDir/../app_build_output/${project.android.defaultConfig.versionName}_${project.android.defaultConfig.versionCode}_${variant.buildType.name}/${productFlavors.name[0]}"
def targetFileNamePrefix
def date = new Date()
def formattedData = date.format('yyyyMMdd-HHmm')
def fileNamePrefix = "${project.name}-${variant.baseName}"
project.logger.error("fileNamePrefix: ${fileNamePrefix}")
if (outputFile != null && outputFile.name.endsWith('.apk')) {
project.logger.error("outputFile: ${outputFile}")
targetFileNamePrefix = "Notta_${project.android.defaultConfig.versionName}_${project.android.defaultConfig.versionCode}_${getGitBranch()}_${variant.buildType.name}_${formattedData}"
project.logger.error("outputFileName: ${targetFileNamePrefix}")
def flavorName=productFlavors.name[0]
if (flavorName=="gp"){
project.copy {
from "${output.outputFile}"
into "${targetDir}/apks"
rename("${fileNamePrefix}.apk", "${targetFileNamePrefix}.apk")
project.logger.error("Copy ${fileNamePrefix}.apk to ${targetDir}/${targetFileNamePrefix}.apk")
}
}else {
// skip cn channel
}
def detailDir="${flavorName}${variant.buildType.name.capitalize()}"
project.copy {
// copy mapping.txt
from "${project.buildDir}/outputs/mapping/${detailDir}/mapping.txt"
into "${targetDir}"
rename("mapping.txt", "${targetFileNamePrefix}-mapping.txt")
project.logger.error("Copy mapping.txt to ${targetDir}/${targetFileNamePrefix}_mapping.txt")
}
project.copy {
from "${project.buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
into "${targetDir}"
rename("R.txt", "${targetFileNamePrefix}-R.txt")
project.logger.error("Copy R.txt to ${targetDir}/${targetFileNamePrefix}_R.txt")
}
}
}
}
}
}
国内apk打包
国内版本的打包可以直接使用美团的walle
官网地址为:https://github.com/Meituan-Dianping/walle
如何打包
在工程根目录下配置
buildscript {
...
dependencies {
...
classpath "com.meituan.android.walle:plugin:1.1.7"
}
}
然后在app目录下配置
apply plugin: 'walle'
walle {
// 指定渠道包的输出路径
apkOutputFolder = new File(project.rootProject.buildDir, "apks")
// 定制渠道包的APK的文件名称
apkFileNameFormat = '${packageName}-${buildType}-v${versionName}-${channel}.apk';
// 渠道配置文件
channelFile = new File("${project.getProjectDir()}/channel")
}
其中channel文件定义了相关的渠道
最后在包时,执行以下指令即可完成打包
gradlew assembleCnOnlineChannels
如何获取各个应用对应的渠道名?
而对应的渠道信息获取方式如下:
ChannelInfo channelInfo= WalleChannelReader.getChannelInfo(this.getApplicationContext());
if (channelInfo != null) {
String channel = channelInfo.getChannel();
Map<String, String> extraInfo = channelInfo.getExtraInfo();
}
// 或者也可以直接根据key获取
String value = WalleChannelReader.get(context, "buildtime");
插入额外信息
walle中的channelFile只支持渠道写入,如果想插入除渠道以外的其他信息,请在walle配置中使用configFile
walle {
// 渠道&额外信息配置文件,与channelFile互斥
configFile = new File("${project.getProjectDir()}/config.json")
}
统一打包脚本
以上我们已经定义了国内和海外版本打包的具体的细节,但是在发版本打包时使用都其实希望操作越简单越好,那么接下来就简单包装一下:
预期目标
只需要输入两个类似的指令打国内和海外版本:“gradlew cnOnline"和"gradlew gpOnline”
海外打包
海外打包指定仅是给assembleGpOnline指令取了个别名
task gpOnline(dependsOn:['assembleGpOnline']){
}
国内打包
国内版本打包需要调整一下,主要原因是因为walle打包的文件输出目录是在根目录下的"./build/apks"在重新编译时会被覆盖,因此我们需要将相关的文件拷到"…/app_build_output"目录下去,具体如下:
task cnOnline(dependsOn:['assembleCnOnlineChannels']){
doLast {
cnArtifact("online")
}
}
def cnArtifact(buildType){
println("cnBuildTask start")
def targetDir = "$rootDir/../app_build_output/${project.android.defaultConfig.versionName}_${project.android.defaultConfig.versionCode}_${buildType}/cn"
def apkDirs="${project.rootDir}/build/apks"
println("target dir is ${targetDir}")
println("apkDirs is ${apkDirs}")
project.copy {
// copy mapping.txt
from apkDirs
into "${targetDir}/apks"
}
}
总结
本篇文章的核心思路是:
- 使用productFlavors配置代码式的多渠道,因此定义了cn和gp两个渠道来分别编译国内和海外的代码
- 国内版本多应用市场的打包借力于walle进行批量输出
欢迎大家点赞加关注,相互交流学习。
本文地址:https://blog.csdn.net/wsx1048/article/details/108742967