增量升级(省流量更新)的Java服务端实现

 By sgwhp (http://blog.csdn.net/sgwhp)转载请注明出处

用过类似360手机助手应该对省流量更新都很熟悉了。详细资料可以参考以下两个帖子:

浅析android应用增量升级

Android应用增量升级


本文需要详细说的是服务端的实现。如果对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 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

//重命名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");
    }
}
比较一下三个文件的大小:

增量升级(省流量更新)的Java服务端实现_第1张图片增量升级(省流量更新)的Java服务端实现_第2张图片增量升级(省流量更新)的Java服务端实现_第3张图片

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(); 
}


你可能感兴趣的:(Linux,JNI,Java)