bsdiff简介
Android增量更新需要用到二进制差分工具:bsdiff
bsdiff and bspatch are tools for building and applying patches to binary files.
编译bsdiff差分合并算法的so库
创建bsdiff差分与合并算法的so库文件,以便Java层通过JNI的方式调用。
bsdiff编译需要bzip库支持,需要引入bzip2的源文件
Makefile1
2
3
4
5
6
7
8
9
10export 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.c1
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
#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
25public 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
47public 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
执行差分合并算法
通过参数传入旧版Apk和新版Apk名称,生成差分包:diff.patch和合并Apk:generated.apk
比较新版Apk与合并Apk的MD5值是一致的,表明bsdiff差分合并成功!
1java -Djava.library.path=. -jar BsDiffAndPatch.jar DiffUpdate_v1.apk DiffUpdate_v2.apk