Android應用增量升級
轉自: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。
相關文章
- Android實現應用的增量更新\升級Android
- Android應用增量升級方案之實踐篇Android
- Android不使用第三方升級庫實現應用升級Android
- JavaFX桌面應用-版本升級Java
- Android技術分享| Bugly 應用升級自定義UIAndroidUI
- Util 應用框架 UI 全新升級框架UI
- React 同構應用 PWA 升級指南React
- 不要升級!不要升級!MacOS 14.4 引發Java 應用崩潰MacJava應用崩潰
- 應用升級SpringCloud版本時的注意事項(Dalston升級到Edgware)SpringGCCloud
- Kubernetes 實戰——升級應用(Deployment)
- 應用身份服務IDaaS重磅升級
- webpack增量打包多頁應用Web
- Android 應用開發者有意推遲升級以收集更多使用者資料Android
- 推動PLM應用升級的幾個要素
- Android應用初級開發——Canavas元件圖形應用Android元件
- SSH原理常見應用升級及埠轉發
- 使用 tableflip 實現應用的優雅熱升級
- ios12升級, App應用崩潰閃退iOSAPP應用崩潰
- 使用Bugly應用升級SDK實現App更新APP
- win10升級後應用打不開怎麼辦_win10升級後應用打不開的解決方法Win10
- android sdk離線升級方法Android
- 管理升級與應用ERP的生命週期(轉)
- 使用 .NET 升級助手將.NET Framework應用遷移到.NET 5Framework
- 微軟免費安卓鎖屏應用升級下載:強大微軟安卓
- Android targetSdkVersion 升級到 26 總結Android
- 升級 Android 認證: 遠端配置Android
- 一款Android App升級庫AndroidAPP
- Android 外掛化 動態升級Android
- android 自動檢測版本升級Android
- Android Target 31 升級全攻略 —— 記阿里首個超級 App 的坎坷升級之路Android阿里APP
- Android 增量編譯小解Android編譯
- 傳統應用系統架構向微服務應用架構升級的實戰案例微服務應用架構
- Android 增量更新完全解析 是增量不是熱修復Android
- 三大特性,多個場景,Serverless 應用引擎 SAE 全面升級Server
- HMS Core 6.0即將釋出 加碼應用生態升級
- 藉助應用的最新創新“升級”您的遊戲業務遊戲
- 【HMS Core】關於應用內購買金鑰升級問題
- AWS RDS強制升級的應對之道——版本升級的最佳實踐