bspatch 官网:http://www.daemonology.net/bsdiff/
bzip2官网:http://www.bzip.org/downloads.html
下载工具包:https://github.com/zhaopingfu/DiffPatchToolsAndLinux/tree/master/tools
EclipseDemo地址:https://github.com/zhaopingfu/DnLsn11_update_server
VSDemo地址:https://github.com/zhaopingfu/DnPfDiff/tree/master/DnPfDiff
客户端Demo地址:https://github.com/zhaopingfu/DnPfLsn12Bspatch
为什么要用增量更新?
普通更新:假如一个旧包是10M,当发现有新版本之后(20M),就将20M下载下来,然后安装
增量更新:假如一个旧包是10M,当发现有新版本之后(20M),之后先生成一个差分包(以二进制的方式比较新包和旧包的差异,相同的地方记录索引,不同的地方记录索引并将内容压缩并记录) ,之后下载下来,然后和手机上已经安装的app进行合并,最后安装
比较之下发现增量更新会比普通更新更加省流量,那么有什么用呢?
如果不用360等其他app商店下载的话,一般都自己搭服务器,就按照阿里云服务器来算的话,1M流量差不多按照5毛钱来算,下载一个包节省10M,1000w的用户的话就是1000w * 10 * 0.5 ,这是一笔很大的生意啊
这里有个问题:假如我们安装完app之后就把安装包删除了,那么下载差分包之后还能合并吗?
答案当然是可以的,因为在我们第一次下载了,安装的时候,系统会帮助我们拷贝一份放在外置卡的data/app/路径下,每一个app按照自己的包名划分,一般没有root的话,这个路径下的文件是没有权限删除的,所以90%的情况下都是可以的,另外的10%是用户将手机root,获得了最高权限,然后将备份的安装包都删除了,这时候就要向服务器反馈了,将全量的包下载下来
生成差分包
步骤:
1、先生成一个windows下的差分工具
VS2013DEMO地址:https://github.com/zhaopingfu/DnPfDiff/tree/master/DnPfDiff
首先下载一个bsdiff4.3-win32-src.zip解压:https://github.com/zhaopingfu/DiffPatchToolsAndLinux/tree/master/tools
创建一个VS项目(这里用的是VS2013的),将平台设置为x64的
在工程路径下创建两个文件夹:src include,将头文件放入include,.c和.cpp放入src里
这里在源文件里找不到头文件,需要一些设置
vs 找头文件 设置:右键工程 ---> 属性 ---> c++ -----> 附含包目录(头文件的目录)
vs 解决 _CRT_SECURE_NO_WARNINGS:右键工程 ---> 属性 ---> c++ -----> 命令行 添加 -D _CRT_SECURE_NO_WARNINGS
vs 关闭sdl 安全检查:右键工程 ---> 属性 ---> c++------>常规 ---->SDL检查 否
vs 切换平台之后 需要重新配置上述依赖
上面都好了之后运行项目,生成一个.exe文件
在cmd中执行这个.exe
PfDiff.exe appOld.apk appNew.apk apk.patch
PFDiff.exe:刚才生成的.exe可执行文件
appOld.apk:旧的安装包
appNew.apk:新的安装包
apk.patch:生成的差分包
执行完之后会看到在当前路径下生成了一个apk.patch差分文件
2、Eclipse中生成差分文件
在Eclipse中创建一个web工程
public class DnPfDiff {
public static native void diff(String oldPath, String newPath, String patchPath);
static {
System.loadLibrary("DnPfDiff");
}
}
之后进入项目的src路径下,生成头文件
javah com.pf.updata.DnPfDiff
之后,将头文件放入VS项目中
这里会发现在头文件中找不到jni.h,这里要找jni.h,注意是window下的
JDK下面的是window下的,SDK下的是linux下的
这里要在JDK里复制jni.h和jni_md.h
在bsdiff.cpp中引入刚才生成的头文件,然后把main方法改个名字,改为bsdiff_main
/*
* Class: com_pf_update_DnPfDiff
* Method: diff
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_pf_update_DnPfDiff_diff
(JNIEnv *env, jclass jcls, jstring old_path_jstr, jstring new_path_jstr, jstring patch_path_jstr)
{
int argc = 4;
char *argv[4];
char *old_path_cstr = (char*)env->GetStringUTFChars(old_path_jstr, NULL);
char *new_path_cstr = (char*)env->GetStringUTFChars(new_path_jstr, NULL);
char *patch_path_cstr = (char*)env->GetStringUTFChars(patch_path_jstr, NULL);
argv[0] = "bsdiff_Pf";
argv[1] = old_path_cstr;
argv[2] = new_path_cstr;
argv[3] = patch_path_cstr;
bsdiff_main(argc, argv);
env->ReleaseStringUTFChars(old_path_jstr, old_path_cstr);
env->ReleaseStringUTFChars(new_path_jstr, new_path_cstr);
env->ReleaseStringUTFChars(patch_path_jstr, patch_path_cstr);
}
之后生成一个.dll动态库,注意不是exe文件
然后放入eclipse工程的根目录中
eclipse里写个main方法即可执行,生成差分包
public class ContantsWin {
// 路径不能包含中文
public static final String BASE_PATH = "E:\\software\\Eclipse\\eclipse-jee-oxygen-R-win32-x86_64\\projects\\DnLsn11_update_server\\WebContent\\download\\";
public static final String OLD_APK_PATH = BASE_PATH + "apkOld.apk";
public static final String NEW_APK_PATH = BASE_PATH + "apkNew.apk";
public static final String PATCH_PATH = BASE_PATH + "apk.patch";
public static final String URL_PATCH_PATH = "/download/apk.patch";
}
public class MainTest {
public static void main(String[] args) {
DnPfDiff.diff(ContantsWin.OLD_APK_PATH, ContantsWin.NEW_APK_PATH, ContantsWin.PATCH_PATH);
}
}
随便写个servlet,就可以将差分包进行下载了
Eclipse下Demo地址:https://github.com/zhaopingfu/DnLsn11_update_server
VS2013DEMO地址:https://github.com/zhaopingfu/DnPfDiff/tree/master/DnPfDiff
3、Linux下生成差分文件
首先下载一个bsdiff4.3-win32-src.zip解压(最上面有链接):https://github.com/zhaopingfu/DiffPatchToolsAndLinux/tree/master/tools
bsdiff.c中的<bzlib.h>修改为"bzlib.h"
除了bsdiff.c中的main方法外,其他的main方法都改成其他的名字
修改权限
chmod 777 ./*
生成一个可执行文件
gcc -fPIC blocksort.c decompress.c bsdiff.c randtable.c bzip2.c huffman.c compress.c bzlib.c crctable.c -o PfBsDiff
执行可执行文件
./PfBsDiff apkOld.apk apkNew.apk apk.patch
生成一个.so动态库(在服务器的后台可以直接使用,安卓客户端不能直接用,需要用NDK进行交叉编译)
gcc -fPIC -shared blocksort.c decompress.c bsdiff.c randtable.c bzip2.c huffman.c compress.c bzlib.c crctable.c -o PfBsDiff.so
合并差分包(客户端完成)
客户端Demo地址:https://github.com/zhaopingfu/DnPfLsn12Bspatch
创建一个Android项目(NDK项目)
首先将bzip下的头文件和源文件和bsdiff中的bspatch.c复制到cpp下(源文件里面的main方法都改名)
配置CMakeLists.txt
写native方法
public class BsPatch {
public native static int patch(String oldfile, String newfile, String patchfile);
static{
System.loadLibrary("PfBisPatch");
}
}
生成头文件,也是上面的javah命令,cmd进入java目录,执行javah com.pf.bspatch.BsPatch
bspatch中引入头文件,并实现头文件中的方法
/*
* Class: com_pf_bspatch_BsPatch
* Method: patch
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_pf_bspatch_BsPatch_patch
(JNIEnv *env, jclass jcls, jstring old_path_jstr, jstring new_path_jstr,
jstring patch_path_jstr) {
int ret = -1;
LOGI("jni patch begin");
char *old_path_cstr = (*env)->GetStringUTFChars(env, old_path_jstr, JNI_FALSE);
char *new_path_cstr = (*env)->GetStringUTFChars(env, new_path_jstr, JNI_FALSE);
char *patch_path_cstr = (*env)->GetStringUTFChars(env, patch_path_jstr, JNI_FALSE);
int argc = 4;
char *argv[4];
argv[0] = "PfBsPatch";
argv[1] = old_path_cstr;
argv[2] = new_path_cstr;
argv[3] = patch_path_cstr;
//如果成功ret等于0
ret = bspatch_main(argc, argv);
LOGI("jni patch end");
(*env)->ReleaseStringUTFChars(env, old_path_jstr, old_path_cstr);
(*env)->ReleaseStringUTFChars(env, new_path_jstr, new_path_cstr);
(*env)->ReleaseStringUTFChars(env, patch_path_jstr, patch_path_cstr);
return ret;
}
cmd里查看下打包生成的包的MD5值和合并生成的包的MD5的值是一样的
查看md5 值
certutil -hashfile apkNew.apk MD5
3a68cc74cf9b0a3841419a54922233d1
certutil -hashfile apk_new_merger.apk MD5
3a68cc74cf9b0a3841419a54922233d1
总结下命令
进入DnPfDiff所在路径下
DnPfDiff.exe apkOld.apk apkNew.apk apk.patch
DnPfDiff.exe:要执行的文件
apkOld.apk:旧的apk
apkNew.apk:新的apk
apk.patch:生成的差分包
///////////////////////////////
进入bspatch所在路径下
bspatch.exe apkOld.apk apkNewMerger.apk apk.patch
bspatch.exe:要执行的文件
apkOld.apk:旧的apk包
apkNewMerger.apk:合并后的文件
apk.patch:差分包
//////////////////////////////////////
linux下生成一个可执行文件
gcc -fPIC blocksort.c decompress.c bsdiff.c randtable.c bzip2.c huffman.c compress.c bzlib.c crctable.c -o PfBsDiff
执行可执行文件
./PfBsDiff apkOld.apk apkNew.apk apk.patch
生成一个.so动态库(在服务器的后台可以直接使用,安卓客户端不能直接用,需要用NDK进行交叉编译)
gcc -fPIC -shared blocksort.c decompress.c bsdiff.c randtable.c bzip2.c huffman.c compress.c bzlib.c crctable.c -o PfBsDiff.so
/////////////////////////////////////////
java中native方法生成头文件
进入java文件所在的根目录
假如是/app/src/main/java/com/pf/bspatch/BsPatch.java的话就进入到java目录下就可以了
在cmd里进入java目录下,执行javah命令(配好JDK之后就可以执行)
javah com.pf.bspatch.BsPatch
这样就会生成一个头文件
///////////////////////////////////
查看md5 值
certutil -hashfile apkNew.apk MD5
3a68cc74cf9b0a3841419a54922233d1