转自:http://blog.csdn.net/tu_bingbing/article/details/8538592
阅读此文之前请先阅读 http://blog.csdn.net/hmg25/article/details/8100896
何为增量升级,简单说下,当应用版本要更新时通常的做法是重新下载新的版本去覆盖旧版本,但这样有个比较明显缺点,太浪费流量了,尤其是在GPRS模式下。我们能不能只更新新版本增加的内容呢,bsdiff/bzlib2可以帮助我们实现这点。下面介绍下具体的做法
第一、生成旧版和新版的差分比patch文件,可以借助bsdiff开源库windows版本
- 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、编写本地方法
- //oldapk_filepath:旧版本存储路径 newapk_savepath:生成的新版本要存放的路径 patchpath:差分比文件存放路径
- public native int applyPatchToOldApk(String oldapk_filepath, String newapk_savepath , String patchpath);
- 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)
- #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。