Android多渠道打包实战

来源:互联网 发布:北海行知学校怎么样 编辑:程序博客网 时间:2024/05/16 10:22

什么是多渠道打包

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的META-INF里添加一个渠道名的空文件,这种方法最早应该是从美团流行开来的。详情抽着:美团Android自动化之旅—生成渠道包
  • 利用zip文件特点,使用APK注释字段保存渠道信息。目前在github上有开源的工具和详细介绍:下一代Android打包工具

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

实战

我司目前使用的是: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 开发者版本

0 0
原创粉丝点击