Android應用增量升級

yangxi_001發表於2016-03-02

轉自: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
  1. 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
  1. //oldapk_filepath:舊版本儲存路徑   newapk_savepath:生成的新版本要存放的路徑  patchpath:差分比檔案存放路徑  
  2. public native int applyPatchToOldApk(String oldapk_filepath, String newapk_savepath , String patchpath);  
3、編寫Android.mk配置檔案,並把需要的bzlib2原始碼檔案()拷貝到目錄下

[html] view plain copy
  1. LOCAL_PATH:= $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3.   
  4. # This is the target being built.  
  5. LOCAL_MODULE:libBsdiff  
  6.   
  7.   
  8. # All of the source files that we will compile.  
  9. # 具體到底需要哪些c程式碼,沒有仔細研究過  
  10. LOCAL_SRC_FILES:tu_bingbing_bsdiff_BsdiffBusiness.c \  
  11.                   bzlib.c \  
  12.                   blocksort.c \  
  13.                   compress.c \  
  14.                   crctable.c \  
  15.                   decompress.c \  
  16.                   huffman.c \  
  17.                   randtable.c \  
  18.                   bzip2.c \  
  19.                           
  20. # No static libraries.  
  21. LOCAL_STATIC_LIBRARIES := \  
  22.      libbz  
  23.   
  24.   
  25. # Also need the JNI headers.  
  26. LOCAL_C_INCLUDES += \  
  27.     $(JNI_H_INCLUDE) external/bzip2  
  28.   
  29. # No special compiler flags.  
  30. LOCAL_CFLAGS +=  
  31.   
  32. include $(BUILD_SHARED_LIBRARY)  
4、實現本地方法

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include "tu_bingbing_bsdiff_BsdiffBusiness.h"  
  3.   
  4. #include "bzlib_private.h"  
  5.   
  6. #include <stdlib.h>  
  7. #include <stdio.h>  
  8. #include <string.h>  
  9. #include <err.h>  
  10. #include <unistd.h>  
  11. #include <fcntl.h>  
  12. #include <android/log.h>  
  13.   
  14.   
  15. static off_t offtin(u_char *buf)  
  16. {  
  17.     off_t y;  
  18.   
  19.     y=buf[7]&0x7F;  
  20.     y=y*256;y+=buf[6];  
  21.     y=y*256;y+=buf[5];  
  22.     y=y*256;y+=buf[4];  
  23.     y=y*256;y+=buf[3];  
  24.     y=y*256;y+=buf[2];  
  25.     y=y*256;y+=buf[1];  
  26.     y=y*256;y+=buf[0];  
  27.   
  28.     if(buf[7]&0x80) y=-y;  
  29.   
  30.     return y;  
  31. }  
  32.   
  33. int applypatch(int argc,char * argv[])  
  34. {  
  35.     FILE * f, * cpf, * dpf, * epf;  
  36.     BZFILE * cpfbz2, * dpfbz2, * epfbz2;  
  37.     int cbz2err, dbz2err, ebz2err;  
  38.     int fd;  
  39.     ssize_t oldsize,newsize;  
  40.     ssize_t bzctrllen,bzdatalen;  
  41.     u_char header[32],buf[8];  
  42.     u_char *old, *new;  
  43.     off_t oldpos,newpos;  
  44.     off_t ctrl[3];  
  45.     off_t lenread;  
  46.     off_t i;  
  47.   
  48.     if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);  
  49.   
  50.     /* Open patch file */  
  51.     if ((f = fopen(argv[3], "r")) == NULL)  
  52.         err(1, "fopen(%s)", argv[3]);  
  53.   
  54.     /* 
  55.     File format: 
  56.         0   8   "BSDIFF40" 
  57.         8   8   X 
  58.         16  8   Y 
  59.         24  8   sizeof(newfile) 
  60.         32  X   bzip2(control block) 
  61.         32+X    Y   bzip2(diff block) 
  62.         32+X+Y  ??? bzip2(extra block) 
  63.     with control block a set of triples (x,y,z) meaning "add x bytes 
  64.     from oldfile to x bytes from the diff block; copy y bytes from the 
  65.     extra block; seek forwards in oldfile by z bytes". 
  66.     */  
  67.   
  68.     /* Read header */  
  69.     if (fread(header, 1, 32, f) < 32) {  
  70.         if (feof(f))  
  71.             errx(1, "Corrupt patch\n");  
  72.         err(1, "fread(%s)", argv[3]);  
  73.     }  
  74.   
  75.     /* Check for appropriate magic */  
  76.     if (memcmp(header, "BSDIFF40", 8) != 0)  
  77.         errx(1, "Corrupt patch\n");  
  78.   
  79.     /* Read lengths from header */  
  80.     bzctrllen=offtin(header+8);  
  81.     bzdatalen=offtin(header+16);  
  82.     newsize=offtin(header+24);  
  83.     if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))  
  84.         errx(1,"Corrupt patch\n");  
  85.   
  86.     /* Close patch file and re-open it via libbzip2 at the right places */  
  87.     if (fclose(f))  
  88.         err(1, "fclose(%s)", argv[3]);  
  89.     if ((cpf = fopen(argv[3], "r")) == NULL)  
  90.         err(1, "fopen(%s)", argv[3]);  
  91.     if (fseeko(cpf, 32, SEEK_SET))  
  92.         err(1, "fseeko(%s, %lld)", argv[3],  
  93.             (long long)32);  
  94.     if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)  
  95.         errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);  
  96.     if ((dpf = fopen(argv[3], "r")) == NULL)  
  97.         err(1, "fopen(%s)", argv[3]);  
  98.     if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))  
  99.         err(1, "fseeko(%s, %lld)", argv[3],  
  100.             (long long)(32 + bzctrllen));  
  101.     if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)  
  102.         errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);  
  103.     if ((epf = fopen(argv[3], "r")) == NULL)  
  104.         err(1, "fopen(%s)", argv[3]);  
  105.     if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))  
  106.         err(1, "fseeko(%s, %lld)", argv[3],  
  107.             (long long)(32 + bzctrllen + bzdatalen));  
  108.     if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)  
  109.         errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);  
  110.   
  111.     if(((fd=open(argv[1],O_RDONLY,0))<0) ||  
  112.         ((oldsize=lseek(fd,0,SEEK_END))==-1) ||  
  113.         ((old=malloc(oldsize+1))==NULL) ||  
  114.         (lseek(fd,0,SEEK_SET)!=0) ||  
  115.         (read(fd,old,oldsize)!=oldsize) ||  
  116.         (close(fd)==-1)) err(1,"%s",argv[1]);  
  117.     if((new=malloc(newsize+1))==NULL) err(1,NULL);  
  118.   
  119.     oldpos=0;newpos=0;  
  120.     while(newpos<newsize) {  
  121.         /* Read control data */  
  122.         for(i=0;i<=2;i++) {  
  123.             lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);  
  124.             if ((lenread < 8) || ((cbz2err != BZ_OK) &&  
  125.                 (cbz2err != BZ_STREAM_END)))  
  126.                 errx(1, "Corrupt patch\n");  
  127.             ctrl[i]=offtin(buf);  
  128.         };  
  129.   
  130.         /* Sanity-check */  
  131.         if(newpos+ctrl[0]>newsize)  
  132.             errx(1,"Corrupt patch\n");  
  133.   
  134.         /* Read diff string */  
  135.         lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);  
  136.         if ((lenread < ctrl[0]) ||  
  137.             ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))  
  138.             errx(1, "Corrupt patch\n");  
  139.   
  140.         /* Add old data to diff string */  
  141.         for(i=0;i<ctrl[0];i++)  
  142.             if((oldpos+i>=0) && (oldpos+i<oldsize))  
  143.                 new[newpos+i]+=old[oldpos+i];  
  144.   
  145.         /* Adjust pointers */  
  146.         newpos+=ctrl[0];  
  147.         oldpos+=ctrl[0];  
  148.   
  149.         /* Sanity-check */  
  150.         if(newpos+ctrl[1]>newsize)  
  151.             errx(1,"Corrupt patch\n");  
  152.   
  153.         /* Read extra string */  
  154.         lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);  
  155.         if ((lenread < ctrl[1]) ||  
  156.             ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))  
  157.             errx(1, "Corrupt patch\n");  
  158.   
  159.         /* Adjust pointers */  
  160.         newpos+=ctrl[1];  
  161.         oldpos+=ctrl[2];  
  162.     };  
  163.   
  164.     /* Clean up the bzip2 reads */  
  165.     BZ2_bzReadClose(&cbz2err, cpfbz2);  
  166.     BZ2_bzReadClose(&dbz2err, dpfbz2);  
  167.     BZ2_bzReadClose(&ebz2err, epfbz2);  
  168.     if (fclose(cpf) || fclose(dpf) || fclose(epf))  
  169.         err(1, "fclose(%s)", argv[3]);  
  170.   
  171.     /* Write the new file */  
  172.     if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||  
  173.         (write(fd,new,newsize)!=newsize) || (close(fd)==-1))  
  174.         err(1,"%s",argv[2]);  
  175.   
  176.     free(new);  
  177.     free(old);  
  178.   
  179.     return 0;  
  180. }  
  181.   
  182. // old 升級之前apk包路徑  
  183. // new 升級之後apk包路徑  
  184. // patch檔案,可以用bsdiff工具生成  
  185. // 具體原理可以參看http://blog.csdn.net/hmg25/article/details/8100896  
  186. JNIEXPORT jint JNICALL Java_tu_bingbing_bsdiff_BsdiffBusiness_applyPatchToOldApk(JNIEnv *env,  
  187.         jobject obj, jstring old, jstring new , jstring patch){  
  188.   int argc=4;  
  189.   char * argv[argc];  
  190.   argv[0]="bspatch";  
  191.   argv[1]=(char*)((*env)->GetStringUTFChars(env,old, 0));  
  192.   argv[2]=(char*)((*env)->GetStringUTFChars(env,new, 0));  
  193.   argv[3]=(char*)((*env)->GetStringUTFChars(env,patch, 0));  
  194.   
  195.   int ret=applypatch(argc, argv);  
  196.   
  197.    (*env)->ReleaseStringUTFChars(env,old,argv[1]);  
  198.    (*env)->ReleaseStringUTFChars(env,new,argv[2]);  
  199.    (*env)->ReleaseStringUTFChars(env,patch,argv[3]);  
  200.    return ret;  
  201. }  

最後 ndk編譯,在Android中呼叫native方法,你會發現在你傳入路徑下生成了新版本的apk。


DEMO:http://download.csdn.net/detail/tibib/5581905

相關文章