您的位置:首页 > 移动开发 > Android开发

Android应用增量升级

2016-03-02 16:40 369 查看
转自:http://blog.csdn.net/tu_bingbing/article/details/8538592

阅读此文之前请先阅读  http://blog.csdn.net/hmg25/article/details/8100896

何为增量升级,简单说下,当应用版本要更新时通常的做法是重新下载新的版本去覆盖旧版本,但这样有个比较明显缺点,太浪费流量了,尤其是在GPRS模式下。我们能不能只更新新版本增加的内容呢,bsdiff/bzlib2可以帮助我们实现这点。下面介绍下具体的做法

第一、生成旧版和新版的差分比patch文件,可以借助bsdiff开源库windows版本

[html] view
plain copy

bsdiff.exe   ../iReader1.6.2.0(v35).apk   ../iReader1.8.0.1(v40).apk   ../ireader.patch  

第二、有了patch文件,我们就可以在Android平台上利用JNI调用bzlib2就可以实现增量升级了。

1、首先要有ndk编译环境,具体怎么搭建详见:http://blog.csdn.net/tibib/article/details/8504680

2、编写本地方法

[java] view
plain copy

//oldapk_filepath:旧版本存储路径   newapk_savepath:生成的新版本要存放的路径  patchpath:差分比文件存放路径  

public native int applyPatchToOldApk(String oldapk_filepath, String newapk_savepath , String patchpath);  

3、编写Android.mk配置文件,并把需要的bzlib2源代码文件()拷贝到目录下

[html] view
plain copy

LOCAL_PATH:= $(call my-dir)  

include $(CLEAR_VARS)  

  

# This is the target being built.  

LOCAL_MODULE:= libBsdiff  

  

  

# All of the source files that we will compile.  

# 具体到底需要哪些c代码,没有仔细研究过  

LOCAL_SRC_FILES:= tu_bingbing_bsdiff_BsdiffBusiness.c \  

                  bzlib.c \  

                  blocksort.c \  

                  compress.c \  

                  crctable.c \  

                  decompress.c \  

                  huffman.c \  

                  randtable.c \  

                  bzip2.c \  

                          

# No static libraries.  

LOCAL_STATIC_LIBRARIES := \  

     libbz  

  

  

# Also need the JNI headers.  

LOCAL_C_INCLUDES += \  

    $(JNI_H_INCLUDE) external/bzip2  

  

# No special compiler flags.  

LOCAL_CFLAGS +=  

  

include $(BUILD_SHARED_LIBRARY)  

4、实现本地方法

[cpp] view
plain copy

#include <stdio.h>  

#include "tu_bingbing_bsdiff_BsdiffBusiness.h"  

  

#include "bzlib_private.h"  

  

#include <stdlib.h>  

#include <stdio.h>  

#include <string.h>  

#include <err.h>  

#include <unistd.h>  

#include <fcntl.h>  

#include <android/log.h>  

  

  

static off_t offtin(u_char *buf)  

{  

    off_t y;  

  

    y=buf[7]&0x7F;  

    y=y*256;y+=buf[6];  

    y=y*256;y+=buf[5];  

    y=y*256;y+=buf[4];  

    y=y*256;y+=buf[3];  

    y=y*256;y+=buf[2];  

    y=y*256;y+=buf[1];  

    y=y*256;y+=buf[0];  

  

    if(buf[7]&0x80) y=-y;  

  

    return y;  

}  

  

int applypatch(int argc,char * argv[])  

{  

    FILE * f, * cpf, * dpf, * epf;  

    BZFILE * cpfbz2, * dpfbz2, * epfbz2;  

    int cbz2err, dbz2err, ebz2err;  

    int fd;  

    ssize_t oldsize,newsize;  

    ssize_t bzctrllen,bzdatalen;  

    u_char header[32],buf[8];  

    u_char *old, *new;  

    off_t oldpos,newpos;  

    off_t ctrl[3];  

    off_t lenread;  

    off_t i;  

  

    if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);  

  

    /* Open patch file */  

    if ((f = fopen(argv[3], "r")) == NULL)  

        err(1, "fopen(%s)", argv[3]);  

  

    /* 

    File format: 

        0   8   "BSDIFF40" 

        8   8   X 

        16  8   Y 

        24  8   sizeof(newfile) 

        32  X   bzip2(control block) 

        32+X    Y   bzip2(diff block) 

        32+X+Y  ??? bzip2(extra block) 

    with control block a set of triples (x,y,z) meaning "add x bytes 

    from oldfile to x bytes from the diff block; copy y bytes from the 

    extra block; seek forwards in oldfile by z bytes". 

    */  

  

    /* Read header */  

    if (fread(header, 1, 32, f) < 32) {  

        if (feof(f))  

            errx(1, "Corrupt patch\n");  

        err(1, "fread(%s)", argv[3]);  

    }  

  

    /* Check for appropriate magic */  

    if (memcmp(header, "BSDIFF40", 8) != 0)  

        errx(1, "Corrupt patch\n");  

  

    /* Read lengths from header */  

    bzctrllen=offtin(header+8);  

    bzdatalen=offtin(header+16);  

    newsize=offtin(header+24);  

    if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))  

        errx(1,"Corrupt patch\n");  

  

    /* Close patch file and re-open it via libbzip2 at the right places */  

    if (fclose(f))  

        err(1, "fclose(%s)", argv[3]);  

    if ((cpf = fopen(argv[3], "r")) == NULL)  

        err(1, "fopen(%s)", argv[3]);  

    if (fseeko(cpf, 32, SEEK_SET))  

        err(1, "fseeko(%s, %lld)", argv[3],  

            (long long)32);  

    if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)  

        errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);  

    if ((dpf = fopen(argv[3], "r")) == NULL)  

        err(1, "fopen(%s)", argv[3]);  

    if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))  

        err(1, "fseeko(%s, %lld)", argv[3],  

            (long long)(32 + bzctrllen));  

    if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)  

        errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);  

    if ((epf = fopen(argv[3], "r")) == NULL)  

        err(1, "fopen(%s)", argv[3]);  

    if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))  

        err(1, "fseeko(%s, %lld)", argv[3],  

            (long long)(32 + bzctrllen + bzdatalen));  

    if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)  

        errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);  

  

    if(((fd=open(argv[1],O_RDONLY,0))<0) ||  

        ((oldsize=lseek(fd,0,SEEK_END))==-1) ||  

        ((old=malloc(oldsize+1))==NULL) ||  

        (lseek(fd,0,SEEK_SET)!=0) ||  

        (read(fd,old,oldsize)!=oldsize) ||  

        (close(fd)==-1)) err(1,"%s",argv[1]);  

    if((new=malloc(newsize+1))==NULL) err(1,NULL);  

  

    oldpos=0;newpos=0;  

    while(newpos<newsize) {  

        /* Read control data */  

        for(i=0;i<=2;i++) {  

            lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);  

            if ((lenread < 8) || ((cbz2err != BZ_OK) &&  

                (cbz2err != BZ_STREAM_END)))  

                errx(1, "Corrupt patch\n");  

            ctrl[i]=offtin(buf);  

        };  

  

        /* Sanity-check */  

        if(newpos+ctrl[0]>newsize)  

            errx(1,"Corrupt patch\n");  

  

        /* Read diff string */  

        lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);  

        if ((lenread < ctrl[0]) ||  

            ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))  

            errx(1, "Corrupt patch\n");  

  

        /* Add old data to diff string */  

        for(i=0;i<ctrl[0];i++)  

            if((oldpos+i>=0) && (oldpos+i<oldsize))  

                new[newpos+i]+=old[oldpos+i];  

  

        /* Adjust pointers */  

        newpos+=ctrl[0];  

        oldpos+=ctrl[0];  

  

        /* Sanity-check */  

        if(newpos+ctrl[1]>newsize)  

            errx(1,"Corrupt patch\n");  

  

        /* Read extra string */  

        lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);  

        if ((lenread < ctrl[1]) ||  

            ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))  

            errx(1, "Corrupt patch\n");  

  

        /* Adjust pointers */  

        newpos+=ctrl[1];  

        oldpos+=ctrl[2];  

    };  

  

    /* Clean up the bzip2 reads */  

    BZ2_bzReadClose(&cbz2err, cpfbz2);  

    BZ2_bzReadClose(&dbz2err, dpfbz2);  

    BZ2_bzReadClose(&ebz2err, epfbz2);  

    if (fclose(cpf) || fclose(dpf) || fclose(epf))  

        err(1, "fclose(%s)", argv[3]);  

  

    /* Write the new file */  

    if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||  

        (write(fd,new,newsize)!=newsize) || (close(fd)==-1))  

        err(1,"%s",argv[2]);  

  

    free(new);  

    free(old);  

  

    return 0;  

}  

  

// old 升级之前apk包路径  

// new 升级之后apk包路径  

// patch文件,可以用bsdiff工具生成  

// 具体原理可以参看http://blog.csdn.net/hmg25/article/details/8100896  

JNIEXPORT jint JNICALL Java_tu_bingbing_bsdiff_BsdiffBusiness_applyPatchToOldApk(JNIEnv *env,  

        jobject obj, jstring old, jstring new , jstring patch){  

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

  

  int ret=applypatch(argc, argv);  

  

   (*env)->ReleaseStringUTFChars(env,old,argv[1]);  

   (*env)->ReleaseStringUTFChars(env,new,argv[2]);  

   (*env)->ReleaseStringUTFChars(env,patch,argv[3]);  

   return ret;  

}  

最后 ndk编译,在Android中调用native方法,你会发现在你传入路径下生成了新版本的apk。

DEMO:http://download.csdn.net/detail/tibib/5581905
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: