Android多渠道打包实战

什么是多渠道打包

BD为了统计营销推广的效果,需要在APK里写入推广渠道,去弄清用户、广告销售是来源于哪个渠道,如是来源于应用宝、百度手机助手这样的应用商店,还是广点通、百度联盟这样的广告平台,以便后续分成结算。因此,开发人员需要为BD提供不同渠道所对应的apk文件。而生成这些不同渠道所对应的APK文件就叫做多渠道打包。

多渠道打包的发展史

从发展历程来看,多渠道打包大约经历了这样几个阶段:

第一阶段:用脚本直接生成渠道apk

在Android开发早期,一般是用eclipse开发,用ant来构建。先创建一个模板文件,如AndroidManifest_t.xml。该模板文件里包含一个渠道的特征串,如“{umeng_channel}”,然后用python之类的脚本替换到渠道特征串,用ant重新构建和签名。现在回想一下,从2011年底到2013年,我竟然在这种方式下使用了2年多 :(
这种方式和目前通过gradle productFlavors的manifestPlaceholders占位符打包实现是基本一致的,只是它又进了一步,把脚本功能也给你实现了。
这种方法每次都需要重新构建和签名,很耗时间。在渠道比较少的时候,还可以接受;当渠道多了,输出几百个渠道包一般都得花费好几个小时,这是无法忍受的。

第二阶段:反编译再签名

通过对第一阶段方法的介绍,我们发现每次都用ant构建项目是最费时的,也不是完全必要的。因此,我们又想出一招来改进:用ApkTool先反编译已apk,然后用脚本替换掉AndroidManifest.xml中的渠道,最后再用jarsigner工具重新签名即可。
这种方法省去了之前ant构建工程的时间,效率得到大大提高。

第三阶段:添加额外信息

这是目前所处的阶段。在这一阶段,目前市面主要有2个做法:

这种方式既不用重新构建工程,也不用重新签名,是目前效率最高的打包方式。

实战

我司目前使用的是:APK注释法。
随着商业的发展,推广渠道越来越多样化,有些渠道已经不复存在,但新的渠道又不断地冒出来,以前固定打包渠道方式导致开发与BD沟通不畅,因此经过讨论,我们采取动态打包方式:给BD提供一个网址,BD自行输入渠道号后生成相应的apk,这样达到BD和开发就进一步“解耦”。

我们的内部网站是用PHP实现的,主要是通过ZipArchive添加相应的API。在服务端生成渠道包的主要代码如下:

copy("uploads/myapp.apk","uploads/myapp_{$channel}.apk");
$zip = new ZipArchive;
$res = $zip->open("uploads/myapp_{$channel}.apk"); 
if ($res === TRUE) { 
    $zip->setArchiveComment(base64_encode($channel)); 
    $zip->close();
    ...

客户端获取渠道的主要代码,请参考Read a zip file comment with Java

String apkPath=context.getPackageCodePath();//获取安装后APK所在路径,普通应用在data/app下
String channel=extractZipChannel(apkPath);

/*
 *以下2个方法实现从APK文件中获取注释内容。
 */
public static String extractZipChannel(String filename) {
    String retStr = null;
    try {
        File file = new File(filename);
        int fileLen = (int) file.length();
        FileInputStream in = new FileInputStream(file);
        /* The whole ZIP comment (including the magic byte sequence)
         * MUST fit in the buffer
         * otherwise, the comment will not be recognized correctly
         *
         * You can safely increase the buffer size if you like
        */
        byte[] buffer = new byte[Math.min(fileLen, 8192)];
        int len;
        in.skip(fileLen - buffer.length);
        if ((len = in.read(buffer)) > 0) {
            retStr = getZipCommentFromBuffer(buffer, len);
        }
        in.close();
    } catch (Exception e) {
         e.printStackTrace();
    }
    return retStr;
 }

 /*
  * magicDirEnd[0x50,0x4b,0x05,0x06]: End of central directory signature.
  * There are 22 bytes between magicDirEnd and the start of the comment, 
  *  and the last 2 bytes are the length of the comment.
  */
 static String getZipCommentFromBuffer(byte[] buffer, int len) {
     byte[] magicDirEnd = {0x50, 0x4b, 0x05, 0x06};
     int buffLen = Math.min(buffer.length, len);
     //Check the buffer from the end
     for (int i = buffLen - magicDirEnd.length - 22; i >= 0; i--) {
         boolean isMagicStart = true;
         for (int k = 0; k < magicDirEnd.length; k++) {
             if (buffer[i + k] != magicDirEnd[k]) {
                 isMagicStart = false;
                 break;
              }
         }
         if (isMagicStart) {
             //Magic Start found!
             int commentLen = buffer[i + 20] + buffer[i + 21] * 256;
             int realLen = buffLen - i - 22;
             String comment = new String(buffer, i + 22, Math.min(commentLen, realLen));
             return comment;
         }
     }
     return null;
  }

关于ZIP文件格式请参考ZIP文件格式分析。下图是该文中对zip文件目录结束标识部分的说明:
zip文件目录结束标识
另外需要说明一点的是,这种方式不能采用最新的APK Signature Scheme v2,您可以在build.gradle中禁用v2签名,即在signingConfigs中相应地添加v2SigningEnabled false

 android {
    ...
    defaultConfig { ... }
    signingConfigs {
      release {
        storeFile file("myreleasekey.keystore")
        storePassword "password"
        keyAlias "MyReleaseKey"
        keyPassword "password"
        v2SigningEnabled false
      }
    }
  }

关于APK Signature Scheme v2,请参考android开发者官网介绍:Android 7.0 开发者版本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值