Android增量更新 - Apk差分合并算法服务端设计

bsdiff简介

Android增量更新需要用到二进制差分工具:bsdiff

bsdiff and bspatch are tools for building and applying patches to binary files.

官方网站:http://www.daemonology.net/bsdiff/
bsdiff for windows:http://www.pokorra.de/coding/bsdiff.html

编译bsdiff差分合并算法的so库

创建bsdiff差分与合并算法的so库文件,以便Java层通过JNI的方式调用。

bsdiff编译需要bzip库支持,需要引入bzip2的源文件

bzip2:http://www.bzip.org/

Makefile

1
2
3
4
5
6
7
8
9
10
export JAVA_HOME = /usr/lib/java/jdk1.8.0_112

CC := gcc
CFLAGS := -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/linux -I /usr/include
LDFLAGS := -fPIC -shared
SOURCE := $(wildcard *.c *.h)
libBsDiffAndPatch.so: $(SOURCE)
$(CC) $(LDFLAGS) $(CFLAGS) $^ -o libBsDiffAndPatch.so
clean:
rm libBsDiffAndPatch.so

cn_appblog_diffpatchserver_BsDiffAndPatch.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include "cn_appblog_diffpatchserver_BsDiffAndPatch.h"
#include "bsdiff.h"
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif

/*
* Class: cn_appblog_diffpatchserver_BsDiffAndPatch
* Method: diff
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_cn_appblog_diffpatchserver_BsDiffAndPatch_diff
(JNIEnv *env, jclass arg, jstring old, jstring new, jstring patch){
printf("-----BsDiffAndPatch_diff start-----\n");

clock_t start, finish;
double duration;
int argc = 4;
char * argv[argc];
argv[0] = "bsdiff";
argv[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0));
argv[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0));
argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));

printf("old apk = %s \n", argv[1]);
printf("new apk = %s \n", argv[2]);
printf("patch = %s \n", argv[3]);

printf("start to generate patch file\n");
start = clock();
int ret = bsdiff_main(argc, argv);
finish = clock();
printf("finish to generate patch file\n");

duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf("BsDiffAndPatch_diff result = %d \n", ret);
printf("time spended %f s \n",duration);

(*env)->ReleaseStringUTFChars(env, old, argv[1]);
(*env)->ReleaseStringUTFChars(env, new, argv[2]);
(*env)->ReleaseStringUTFChars(env, patch, argv[3]);

printf("-----BsDiffAndPatch_diff end-----\n");
return ret;
}

/*
* Class: cn_appblog_diffpatchserver_BsDiffAndPatch
* Method: patch
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_cn_appblog_diffpatchserver_BsDiffAndPatch_patch
(JNIEnv *env, jclass arg, jstring old, jstring new, jstring patch){
printf("-----BsDiffAndPatch_patch start-----\n");

clock_t start, finish;
double duration;
int argc = 4;
char * argv[argc];
argv[0] = "bspatch";
argv[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0));
argv[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0));
argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));

printf("old apk = %s \n", argv[1]);
printf("patch = %s \n", argv[3]);
printf("new apk = %s \n", argv[2]);

printf("start to apply patch file\n");
start = clock();
int ret = bspatch_main(argc, argv);
finish = clock();
printf("finish to apply patch file\n");

duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf("BsDiffAndPatch_patch result = %d \n", ret);
printf("time spended %f s \n",duration);

(*env)->ReleaseStringUTFChars(env, old, argv[1]);
(*env)->ReleaseStringUTFChars(env, new, argv[2]);
(*env)->ReleaseStringUTFChars(env, patch, argv[3]);

printf("-----BsDiffAndPatch_patch end-----\n");
return ret;
}

#ifdef __cplusplus
}
#endif

Java层调用so库实现diff和patch

Java层采用JNI方式调用so库封装的diff和patch方法

native方法声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class BsDiffAndPatch {

/**
* 差分
* @param oldApkPath
* @param newApkPath
* @param patchPath
* @return
*/
public native static int diff(String oldApkPath, String newApkPath, String patchPath);

/**
* 合并
* @param oldApkPath
* @param newApkPath
* @param patchPath
* @return
*/
public static native int patch(String oldApkPath, String newApkPath, String patchPath);

static {
System.loadLibrary("BsDiffAndPatch");
}

}

主函数调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class DiffPatchServer {

// patch存储路径
public static final String PATCH_FILE_PATH = "diff.patch";
// 使用旧版本+patch包,合成的新版本
public static final String OLD_TO_NEW_APK_PATH = "generated.apk";

private static String oldApkPath = null;
private static String newApkPath = null;

public static void main(String[] args) {
if (args.length != 2) {
System.out.println("抱歉,参数不正确!");
return;
}
System.out.println("\n\n------ Android Apk差分合并算法服务端演示 ------");
String os = System.getProperty("os.name");
System.out.println("操作系统:" + os);
oldApkPath = args[0];
newApkPath = args[1];
System.out.println("旧版Apk:" + oldApkPath);
System.out.println("新版Apk:" + newApkPath);

System.out.println();
generateDiff();
System.out.println();
applyPatch();
System.out.println();
}

public static void generateDiff() {
System.out.println("正在生成差分文件,请稍后...");
BsDiffAndPatch.diff(oldApkPath, newApkPath, PATCH_FILE_PATH);
}

public static void applyPatch() {
System.out.println("正在创建合并文件,请稍后...");
BsDiffAndPatch.patch(oldApkPath, OLD_TO_NEW_APK_PATH, PATCH_FILE_PATH);
try {
System.out.println("新版Apk MD5值:" + MD5Util.getFileMD5String(newApkPath));
System.out.println("合并Apk MD5值:" + MD5Util.getFileMD5String(OLD_TO_NEW_APK_PATH));
} catch (IOException e) {
e.printStackTrace();
}
}

}

生成jar包

注意指定 Main Class

image

执行差分合并算法

通过参数传入旧版Apk和新版Apk名称,生成差分包:diff.patch和合并Apk:generated.apk

比较新版Apk与合并Apk的MD5值是一致的,表明bsdiff差分合并成功!

1
java -Djava.library.path=. -jar BsDiffAndPatch.jar DiffUpdate_v1.apk DiffUpdate_v2.apk

image

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :