Android 多渠道打包

我想,每当发布新版本时,大家都会对那么多发布渠道感到头疼不已,本文主要介绍三种Android的多渠道打包方式。下面的例子我以 TalkingData 统计为例子。

1. Gradle 配置 Flavor

1.1 在 AndroidManifest.xml 中配置 meta-data 标签:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="your.package.name">
 <application>
   <meta-data android:name="TD_CHANNEL_ID" android:value="${TD_CHANNEL_VALUE}"/>
  </application> 
</manifest>
1.2 在项目的 build.gradle 里配置 productFlavors,定义渠道号:
productFlavors {
  yyb {}  
  bdsjzs {}  
  wdj {}  
  xiaomi {}  
  sjzs360 {} 
  productFlavors.all {  
      flavor -> flavor.manifestPlaceholders = [TD_CHANNEL_VALUE: name] 
   }
}

上面定义了一些渠道(比如:豌豆荚,360,小米等),TD_CHANNEL_VALUE 就是你在 meta-data 标签中配置的值。
同时,需要注意的是,你需要在 defaultConfig 中配置一个默认的渠道名称:
manifestPlaceholders = [TD_CHANNEL_VALUE: "default_channel_name"]

该原理就是在打包过程中更换 meta-data 标签中的内容。

优势:方便理货,可以根据自身的需求配置不同的渠道执行不同的逻辑(比如:小米渠道有一个独立的方法,其他渠道没有);
劣势:打包速度慢(打包时是对每个渠道都进行一次完整的打包过程)。

2.第三方打包工具

第三方的打包工具原理跟 Gradle 一样,也是去修改 AndroidManifest.xml 中的 meta-data 标签的内容,然后执行N次打包签名的操作实现多渠道打包的。

优势:简单方便,几乎不用自身做什么工作;
劣势:打包速度过慢;

3.美团的多渠道打包方案

多渠道打包原理可以参考美团Android自动化之旅—生成渠道包这篇文章。Demo 可以查看 快速多渠道打包

3.1 实现原理

直接解压apk,解压后的根目录会有一个META-INF
目录,如下图所示:



如果在META-INF目录内添加空文件,可以不用重新签名应用。因此,通过为不同渠道的应用添加不同的空文件,可以唯一标识一个渠道。

这样,每打一个渠道包只需复制一个apk,在 META-INF 中添加一个使用渠道号命名的空文件即可。这种打包方式速度非常快,900多个渠道不到一分钟就能打完。

2.2 多渠道实现步骤

2.2.1 android 代码中获取渠道号
/**
 * 渠道号工具类:解析压缩包,从中获取渠道号
 */
public class ChannelUtil {
 private static final String CHANNEL_KEY = "uuchannel";
 private static final String DEFAULT_CHANNEL = "internal";
 private static String mChannel;

 public static String getChannel(Context context) {
     return getChannel(context, DEFAULT_CHANNEL);
 }

 public static String getChannel(Context context, String defaultChannel) {
     if (!TextUtils.isEmpty(mChannel)) {
         return mChannel;
     }
     //从apk中获取
     mChannel = getChannelFromApk(context, CHANNEL_KEY);
     if (!TextUtils.isEmpty(mChannel)) {
        return mChannel;
     } //全部获取失败
   return defaultChannel;
 }
/**
 * 从apk中获取版本信息
 *
 * @param context
 * @param channelKey
 * @return
 */
 private static String getChannelFromApk(Context context, String channelKey) {
 long startTime = System.currentTimeMillis();
 //从apk包中获取
 ApplicationInfo appinfo = context.getApplicationInfo();
 String sourceDir = appinfo.sourceDir;
 //默认放在meta-inf/里, 所以需要再拼接一下
 String key = "META-INF/" + channelKey;
 String ret = "";
 ZipFile zipfile = null;
 try {
     zipfile = new ZipFile(sourceDir);
     Enumeration<?> entries = zipfile.entries();
     while (entries.hasMoreElements()) {
         ZipEntry entry = ((ZipEntry) entries.nextElement());
         String entryName = entry.getName();
         if (entryName.startsWith(key)) {
             ret = entryName; break;
         }
      }
 } catch (IOException e) {
       e.printStackTrace();
 } finally {
       if (zipfile != null) {
           try {
                 zipfile.close();
           } catch (IOException e) {
                 e.printStackTrace();
           }
       }
}
String channel = ""; 
if (!TextUtils.isEmpty(ret)) {
     String[] split = ret.split("_");
     if (split != null && split.length >= 2) {
         channel = ret.substring(split[0].length() + 1);
      }
    System.out.println("-----------------------------");
    System.out.println("渠道号:" + channel + ",解压获取渠道号耗时:" + (System.currentTimeMillis() - startTime) + "ms"); 
    System.out.println("-----------------------------");
 } else { 
    System.out.println("未解析到相应的渠道号,使用默认内部渠道号");
    channel = DEFAULT_CHANNEL;
 } 
return channel;
}

然后在代码中初始化调用TalkingData设置渠道号:

String channelName = ChannelUtil.getChannel(mContext);
TCAgent.init(mContext,Constants.APP_KEY_TD,channelName );
2.2.2 编写渠道号文件

修改 info/channel.txt(一行代表一个渠道):

yyb
bdsjzs
wdj
xiaomi
...
2.2.3 编写 python 脚本

实现解压 apk 文件,遍历渠道文件 为 META-INF 目录添加新文件,重新压缩 apk 文件等逻辑:

# coding=utf-8
import zipfile
import shutil
import os

def delete_file_folder(src):
 '''delete files and folders'''
 if os.path.isfile(src):
   try:
     os.remove(src)
   except:
     pass 
elif os.path.isdir(src):
   for item in os.listdir(src):
     itemsrc=os.path.join(src,item)
     delete_file_folder(itemsrc)
   try:
     os.rmdir(src)
   except: 
      pass

# 创建一个空文件,此文件作为apk包中的空文件 
 src_empty_file = 'info/empty.txt'
 f = open(src_empty_file,'w')
 f.close()

# 在渠道号配置文件中,获取指定的渠道号
 channelFile = open('./info/channel.txt','r')
 channels = channelFile.readlines()
 channelFile.close()
 print('-'*20,'all channels','-'*20)
 print(channels)print('-'*50)

# 获取当前目录下所有的apk文件
 src_apks = [];
 for file in os.listdir('.'):
   if os.path.isfile(file):
     extension = os.path.splitext(file)[1][1:]
       if extension in 'apk': 
          src_apks.append(file)

# 遍历所以的apk文件,向其压缩文件中添加渠道号文件
for src_apk in src_apks:
 src_apk_file_name = os.path.basename(src_apk)
 print('current apk name:',src_apk_file_name)
 temp_list = os.path.splitext(src_apk_file_name)
 src_apk_name = temp_list[0]
 src_apk_extension = temp_list[1]
 apk_names = src_apk_name.split('-');
 output_dir = 'outputDir'+'/'
 if os.path.exists(output_dir):
   delete_file_folder(output_dir)
 if not os.path.exists(output_dir):
   os.mkdir(output_dir)

 # 遍历从文件中获得的所以渠道号,将其写入APK包中
 for line in channels:
   target_channel = line.strip()
   target_apk = output_dir + apk_names[0] + "-" + target_channel+"-"+apk_names[2] + src_apk_extension
   shutil.copy(src_apk, target_apk)
   zipped = zipfile.ZipFile(target_apk, 'a', zipfile.ZIP_DEFLATED)
   empty_channel_file = "META-INF/uuchannel_{channel}".format(channel = target_channel)
   zipped.write(src_empty_file, empty_channel_file)
   zipped.close()

print('-'*50)
print('repackaging is over ,total package: ',len(channels))
input('\npackage over...')
2.2.4 打包一个签名 APK 包

将APK 包放在 python 脚本的同级目录

2.2.5 执行python 脚步,多渠道打包
python Android.py

最后就会在 同级目录下生成 outputDir 目录,里面就是各个渠道的 APK 。

优势:打包速度很快,很方便;
劣势:不够灵活,不能灵活的配置不同的渠道不同的业务逻辑;

总结
三种打包方式中,各有各的优势,大家在选择多渠道打包方式的时候可以根据自身的需求来选择

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • Android多渠道打包 概述 每当发新版本时,Android客户端会被分发到各个应用市场,比如豌豆荚,360手机...
    砺雪凝霜阅读 2,121评论 2 11
  • 目录一、Python打包及优化(美团多渠道打包)二、Gradle打包三、其他打包方案:修改Zip文件的commen...
    守望君阅读 5,680评论 4 17
  • 我们做Android用户级应用开发的时候都要考虑这样的问题,目前的应用市场有很多,我们的安装包是通过哪个渠道进入用...
    尹star阅读 8,753评论 11 26
  • 这里主要说一下同时使用 微信资源混淆以及同时使用 walle 打多渠道包 遇到的坑,以及如何解决这个问题的。如果只...
    Coralline_xss阅读 1,196评论 0 1
  • android多渠道打包 1.如何查看apk的签名信息 1将apk解压; 2.找到META-INF 下的.RSA文...
    ping0505阅读 1,033评论 0 1