0
点赞
收藏
分享

微信扫一扫

Andorid快速打包工具Package_Ng 简介


  • ​​背景​​
  • ​​原理​​
  • ​​实现​​
  • ​​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末尾的

Andorid快速打包工具Package_Ng 简介_jar

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

Andorid快速打包工具Package_Ng 简介_脚本_02

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

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

下面给出PACKER_Ng中的类结构

Andorid快速打包工具Package_Ng 简介_数据_03

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

Andorid快速打包工具Package_Ng 简介_脚本_04

实现

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 再也不担心我的多渠道打包!!!)

Andorid快速打包工具Package_Ng 简介_jar_05

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​​


举报

相关推荐

0 条评论