Andorid快速打包工具Package_Ng 简介

来源:互联网 发布:房价数据网 编辑:程序博客网 时间:2024/06/07 17:39

        • 背景
        • 原理
        • 实现
        • package_ng在as中配置
      • 使用特别注意事项

背景

我们在日常使用应用可能会遇到以下场景。
场景1:多渠道打包,算下公司渠道有100多个,每次发版打包就要花掉1个多小时。发版中出现bug需要及时修复,就需要重复打包。严重影响上线效率。那么有没有一种技术能代码gradle脚本,实现快速打包呢?答案是肯定的

Package_ng链接地址:
https://github.com/mcxiaoke/packer-ng-plugin

原理

android使用的apk包的压缩方式是zip,与zip有相同的文件结构,在zip的Central directory file header中包含一个File comment区域,可以存放一些数据。File comment是zip文件如果可以正确的修改这个部分,就可以在不破坏压缩包、不用重新打包的的前提下快速的给apk文件写入自己想要的数据。
comment是在Central directory file header末尾储存的,可以将数据直接写在这里,下表是header末尾的

这里写图片描述

由于数据是不确定的,我们无法知道comment的长度,从表中可以看到zip定义comment的长度的位置在comment之前,所以无法从zip中直接获取comment的长度。这里我们需要自定义comment的长度,在自定义comment内容的后面添加一个区域储存comment的长度,结构如下图。

这里写图片描述

这里可以将一个固定的结构写在comment中,然后根据自定义的长度分区获取每个部分的内容,还可以添加其它数据,如校验码、版本等。

具体源码介绍:
查看master中的Pack_ng 类,核心逻辑都在这个类中。

下面给出PACKER_Ng中的类结构

这里写图片描述

上面圈红的类就是具体的读写marker内容的类

这里写图片描述

实现

1.将数据写入comment

这一部分可以在本地进行,需要定义一个长度为2的byte[]来储存comment的长度,直接使用Java的api就可以把comment和comment的长度写到apk的末尾,代码如下。

public static void writeApk(File file, String comment) {    ZipFile zipFile = null;    ByteArrayOutputStream outputStream = null;    RandomAccessFile accessFile = null;    try {        zipFile = new ZipFile(file);        String zipComment = zipFile.getComment();        if (zipComment != null) {            return;        }        byte[] byteComment = comment.getBytes();        outputStream = new ByteArrayOutputStream();        outputStream.write(byteComment);        outputStream.write(short2Stream((short) byteComment.length));        byte[] data = outputStream.toByteArray();        accessFile = new RandomAccessFile(file, "rw");        accessFile.seek(file.length() - 2);        accessFile.write(short2Stream((short) data.length));        accessFile.write(data);    } catch (IOException e) {        e.printStackTrace();    } finally {        try {            if (zipFile != null) {                zipFile.close();            }            if (outputStream != null) {                outputStream.close();            }            if (accessFile != null) {                accessFile.close();            }        } catch (Exception e) {        }    }}

2.读取apk包中的comment数据

首先获取apk的路径,通过context中的getPackageCodePath()方法就可以获取,代码如下。

public static String getPackagePath(Context context) {    if (context != null) {        return context.getPackageCodePath();    }    return null;}

获取路径之后就可以读取comment的内容了,这里不能直接使用ZipFile中的getComment()方法直接获取comment,因为这个方法是Java7中的方法,在android4.4之前是不支持Java7的,所以我们需要自己去读取apk文件中的comment。首先根据之前自定义的结构,先读取写在最后的comment的长度,根据这个长度,才可以获取真正comment的内容,代码如下。

public static String readApk(File file) {    byte[] bytes = null;    try {        RandomAccessFile accessFile = new RandomAccessFile(file, "r");        long index = accessFile.length();        bytes = new byte[2];        index = index - bytes.length;        accessFile.seek(index);        accessFile.readFully(bytes);        int contentLength = stream2Short(bytes, 0);        bytes = new byte[contentLength];        index = index - bytes.length;        accessFile.seek(index);        accessFile.readFully(bytes);        return new String(bytes, "utf-8");    } catch (FileNotFoundException e) {        e.printStackTrace();    } catch (IOException e) {        e.printStackTrace();    }    return null;}

这里的stream2Short()和short2Stream()参考了MultiChannelPackageTool中的方法。
然后就是断点查看了。看结果28s 时间打了 100 +个渠道包。(mama 再也不担心我的多渠道打包!!!)

这里写图片描述

3.package_ng在as中配置

Eg:这里以umeng 服务为样本

一般卸载app的入口处,Application中
/设置渠道/
market = PackerNg.getMarket(context); //读取apk中的渠道
AnalyticsConfig.setChannel(market); //调用umeng的api 进行渠道设置

Build.gradle中的配置 主要配置Pg依赖

项目 root 下的Build.gradle

buildscript {    repositories {        jcenter()    }    dependencies {        classpath 'com.android.tools.build:gradle:2.2.2'        classpath 'com.mcxiaoke.gradle:packer-ng:1.0.8'        // NOTE: Do not place your application dependencies here; they belong        // in the individual module build.gradle files    }}allprojects {    repositories {        jcenter()    }}task clean(type: Delete) {    delete rootProject.buildDir}

app root 下的build.gradle文件

apply plugin: 'packer'dependencies {    compile fileTree(include: ['*.jar'], dir: 'libs')    compile files('libs/android-support-v4.jar')    compile files('libs/gson-2.2.2.jar')    compile files('libs/nineoldandroids.jar')    compile files('libs/systembartint-1.0.4.jar')    compile files('libs/umeng-analytics-v5.5.3.jar')    compile files('libs/umeng-update-v2.6.0.1.jar')    compile files('libs/universal-image-loader-1.9.3.jar')    compile 'com.mcxiaoke.gradle:packer-helper:1.0.8'    compile 'de.greenrobot:greendao:1.3.7'    compile project(':PushSDK')}

使用特别注意事项

近期使用packet_ng 打渠道包,出现360应用市场统计不到渠道信息问题,具体原因是由于 360加固导致。从新查看的原作者文章看到有具体说明,一定要看完整文档!!!!!

本人使用packet_ng jar 进行命令行打包,发现打出来的包渠道信息乱码,最后选择使用Helper 类中的api (writer reader)

    @Test    public void readZipComment() {        try {            String name = "app-release1_252_jiagu_sign.apk";            File file = new File("E:\\2.5.5\\app\\build\\outputs\\apk\\" + name + "");            String mart = PackerNg.Helper.readMarket(file);            Log.d("readZipComment", mart);        } catch (IOException e) {            Log.d("", "readZipComment: " + e.getMessage());        }    }    @Test    public void WriterZipComment() {        try {            String name = "app-release1_252_jiagu_sign.apk";            String channelName = "360dev";            File file = new File("E:\\2.5.5\\app\\build\\outputs\\apk\\" + name + "");            PackerNg.Helper.writeMarket(file, "360dev");            Log.d("readZipComment", channelName);        } catch (IOException e) {            Log.d("", "readZipComment: " + e.getMessage());        }    }

进行读写,不会出现乱码问题的,该问题已经联系作者,日后更新,特此更正

具体详细细节可以参照package_ng 作者介绍。

使用中有什么问题可以联系:微信

这里写图片描述

快速打包原理:
http://pingguohe.net/2016/03/21/Dynimac-write-infomation-into-apk.html?utm_source=tuicool&utm_medium=referral

1 0