By sgwhp (http://blog.csdn.net/sgwhp)转载请注明出处
用过类似360手机助手应该对省流量更新都很熟悉了。详细资料可以参考以下两个帖子:
本文需要详细说的是服务端的实现。如果对JNI还不了解的,建议先了解了JNI再看本文。
1、准备工具
(1)bsdiff源码(点击下载)、某个应用的两个不同版本。或者直接下载上面提到的第一个博主提供的工具和素材。点击打开链接 这里面包括了我们需要用到的bsdiff源码和apk等。
(2)除此之外,还需要下载bzip2。点击打开链接
2、编译环境
最好在Linux下编译。up主在Windows下面折腾过,表示装了MinGW也没用,很多库都没有。如果确实要在Windows下编译,可以参考这个GitHub项目到Compiling说明。当然了,都提到了JNI,Java的环境肯定也是必不可少的。
3、编码实现
(1)创建Native方法类:up主用eclipse在test包下创建了JDiff.java类
package test;
public class JDiff {
public static native int genDiff(String oldPath, String newPath , String patchPath);
}
(2)编译JDiff,并生成头文件:直接通过终端cd到JDiff.java所在目录,用javac编译。由于有package的存在,cd到src同级目录(上级目录),通过以下命令生成头文件。
javah -jni test.JDiff
成功到话会在src目录下生成test_JDiff.h。如果是在Windows下编译,不要-jni参数也是可以的。
(3)实现本地方法
解压下载到的bsdiff压缩包,提取其中到bsdiff.c到src目录下,名字随便取,为了保持一致,up主命名为“test_JDiff.c”。接下来根据我们刚刚生成到头文件对这个文件进行改造,实现我们定义到genDiff()方法。最简单的,我们将其中的main方法改名,这里命名为“genDiff”,加入头文件申明的genDiff()方法。最后还要导入“test_JDiff.h”和“jni.h”。具体实现如下:
//导入库
#include <jni.h>
#include <test_JDiff.h>
#include <sys/types.h>
#include <bzlib.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
//重命名main方法
int genDiff(int argc,char *argv[]) {...}
//实现本地方法
JNIEXPORT jint JNICALL Java_test_JDiff_genDiff
(JNIEnv *env, jclass cls, jstring old, jstring new, jstring patch){
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));
int ret=genpatch(argc, argv);
(*env)->ReleaseStringUTFChars(env,old,argv[1]);
(*env)->ReleaseStringUTFChars(env,new,argv[2]);
(*env)->ReleaseStringUTFChars(env,patch,argv[3]);
return ret;
}
(4)编译生成so
前面下载到bzip2到这儿就派上用场了,解压并提取其中的bzlib.c、bzip2.c、crctable.c、blocksort.c、compress.c、decompress.c、huffman.c、randtable.c(没有研究具体需要哪几个,前三个肯定是必需的,有兴趣的童鞋可以折腾折腾),同样是解压到src目录下。最后用gcc编译:
gcc -I/usr/lib/jvm/java-7-sun/include/linux/ -I/usr/lib/jvm/java-7-sun/include/
-I/home/robust/workspace/Test/src -fPIC -shared -o libtest_JDiff.so
test_JDiff.c bzlib.c bzip2.c blocksort.c compress.c crctable.c decompress.c huffman.c randtable.c
这个命令太长,为了方便查看,已经截断了。真正编译的时候不要有回车。简单说明这个gcc命令的几个参数:
“-I/usr/lib/jvm/java-7-sun/include/linux/” 和 “-I/usr/lib/jvm/java-7-sun/include/”把JNI的库引进来;
“-I/home/robust/workspace/Test/src”指名当前到路径,否则编译器找不到bzlib.c等文件的路径。
“libtest_JDiff.so”在Linux下,so的名称必需以lib开头,后面才是这个动态链接库真正的名称。
把生成的libtest_JDiff.so移至项目到根目录下。
(5)Java调用Native方法
准备某个应用的两个不同版本的apk,测试一下这个Native方法是否完成。编写一个测试类:
package test;
public class Test {
static{
System.loadLibrary("test_JDiff");//装载动态链接库,记得把开头的“lib”去掉
}
public static void main(String[] args){
JDiff.genDiff("old.apk", "new.apk", "patch.patch");
System.out.println("finished");
}
}
比较一下三个文件的大小:
4、后话
由于增量包是的生成代码是在Linux下编译的,所以也只能在Linux下运行。如果要在Windows下运行,可以考虑以下两种方法:
(1)在Windows下重新编译一次,具体方法前面已经提到。
(2)使用这个包里的bsdiff.exe,通过Java的Runtime来调用。假设我们所有的文件,包括bsdiff.exe和其他apk都在d盘根目录,那么代码实现可以如下:
Runtime rt = Runtime.getRuntime();
try {
rt.exec("d:/bsdiff d:/old.apk d:/new.apk d:/patch.patch");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}