日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

【Android-功能】Android应用增量更新

發(fā)布時(shí)間:2025/4/16 Android 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Android-功能】Android应用增量更新 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

很久沒有更新博客了,真是墮落啊,幾次想提起筆,卻總是被各種瑣事耽擱,以后會(huì)多寫文章記錄點(diǎn)滴。

背景

隨著android應(yīng)用體積的不斷增大,以及應(yīng)用版本發(fā)布的不斷更迭,用戶的升級(jí)成了一個(gè)問題,google也意識(shí)到不斷更新應(yīng)用對(duì)用戶流量的損耗,在Google I/O 上提及的 Smart App update,即應(yīng)用增量升級(jí),或者叫做差分升級(jí)的做法,并在新版本的Google Play中得到支持,某天在和群友聊天是扯到這方面的話題,好奇就稍微研究了一下。

增量升級(jí)的原理

今天我們就來實(shí)現(xiàn)類似的應(yīng)用的增量升級(jí)。其實(shí)增量升級(jí)的原理很簡(jiǎn)單,即首先將應(yīng)用的舊版本Apk與新版本Apk做差分,得到更新的部分的補(bǔ)丁,例如舊版本的APK有5M,新版的有8M,更新的部分則可能只有3M左右(這里需要說明的是,得到的差分包大小并不是簡(jiǎn)單的相減,因?yàn)槠鋵?shí)需要包含一些上下文相關(guān)的東西),使用差分升級(jí)的好處顯而易見,那么你不需要下載完整的8M文件,只需要下載更新部分就可以,而更新部分可能只有3、4M,可以很大程度上減少流量的損失。 在用戶下載了差分包之后,需要在手機(jī)端將他們組合起來。可以參考的做法是先將手機(jī)端的舊版本軟件(多半在/data/下),復(fù)制到SD卡或者cache中,將它們和之前的差分patch進(jìn)行組合,得到一個(gè)新版本的apk應(yīng)用,如果不出意外的話,這個(gè)生成的apk和你之前做差分的apk是一致的。

增量升級(jí)的操作

在了解基本的原理之后,我們來逐步解決其中的各個(gè)難點(diǎn)。首先是差分包patch的生成。如果做過android手機(jī)OTA升級(jí)的同學(xué)應(yīng)該注意到,在update.zip中的patch文件夾中有需要與系統(tǒng)文件同名但是以xxx.p 為后綴的文件,他們就是生成的差分patch文件。我們可以借鑒OTA系統(tǒng)升級(jí)的差分生成工具來生成我們單個(gè)應(yīng)用apk的差分patch文件。 OTA系統(tǒng)差分包的制作,使用命令: [html]?view plaincopyprint?
  • ./build/tools/releasetools/ota_from_target_files?-n?-i?<舊包>?<新包>?<差分包名>??
  • 在查閱ota_from_target_files 的代碼可知,是在函數(shù)WriteIncrementalOTAPackage里生成差分包的,在這個(gè)函數(shù)里邊創(chuàng)建了common.Difference這個(gè)類,我們繼續(xù)跟進(jìn),在common.py中的類 ? class Difference(object):里可以看到: ? ? [html]?view plaincopyprint?
  • diff_program?=?DIFF_PROGRAM_BY_EXT.get(ext,?"bsdiff")??
  • ? ? ? ?至此我們就看到了android中提供我們用來制作差分增量升級(jí)包的工具,"bsdiff",這是一個(gè)很牛X開源的二進(jìn)制差分工具.相關(guān)的介紹傳送門 相關(guān)的代碼地址?或者在android的代碼目錄下 \external\bsdiff bsdiff是二進(jìn)制差分工具,其對(duì)應(yīng)的bspatch是相應(yīng)的補(bǔ)丁合成工具 需要注意的是增量升級(jí)的補(bǔ)丁包,是需要在服務(wù)器端,即PC端完成,大致流程如,制作補(bǔ)丁時(shí)調(diào)用bsdiff函數(shù),根據(jù)兩個(gè)不同版本的二進(jìn)制文件,生成補(bǔ)丁文件。? [html]?view plaincopyprint?
  • 命令:bsdiff?oldfile?newfile?patchfile??
  • 例如:?bsdiff?xx_v1.0.apk?xx_v2.0.apk?xx.patch??
  • 將生成的補(bǔ)丁包 xx.patch放置在升級(jí)服務(wù)器上,供用戶下載升級(jí),對(duì)應(yīng)多版本需要對(duì)不同的版本進(jìn)行差分,對(duì)于版本跨度較大的,建議整包升級(jí)。 用戶在下載了 xx.patch補(bǔ)丁包后,需要用到補(bǔ)丁所對(duì)應(yīng)的apk,即原來系統(tǒng)安裝的舊版本apk和補(bǔ)丁合成的bspatch工具。系統(tǒng)舊版本的apk可以通過copy系統(tǒng)data/app目錄下的apk文件獲取,而補(bǔ)丁合成的bspatch可以通過將bspatch源碼稍作修改,封裝成一個(gè)so庫(kù),供手機(jī)端調(diào)用。 [html]?view plaincopyprint?
  • bspatch的命令格式為:??
  • bspatch?oldfile?newfile?patchfile??
  • 和差分時(shí)的參數(shù)一樣。合成新的apk便可以用于安裝。 以上只是簡(jiǎn)單的操作原理,增量升級(jí)還涉及很多其他方面,例如,升級(jí)補(bǔ)丁校驗(yàn)等問題,可以參考android源碼中bootable\recovery\applypatch的相關(guān)操作,本文只是淺析,在此不表。

    不足

    增量升級(jí)并非完美無(wú)缺的升級(jí)方式,至少存在以下兩點(diǎn)不足: 1.增量升級(jí)是以兩個(gè)應(yīng)用版本之間的差異來生成補(bǔ)丁的,你無(wú)法保證用戶每次的及時(shí)升級(jí)到最新,所以你必須對(duì)你所發(fā)布的每一個(gè)版本都和最新的版本作差分,以便使所有版本的用戶都可以差分升級(jí),這樣操作相對(duì)于原來的整包升級(jí)較為繁瑣,不過可以通過自動(dòng)化的腳本批量生成。 2.增量升級(jí)成功的前提是,用戶手機(jī)端必須有能夠讓你拷貝出來且與你服務(wù)器用于差分的版本一致的apk,這樣就存在,例如,系統(tǒng)內(nèi)置的apk無(wú)法獲取到,無(wú)法進(jìn)行增量升級(jí);對(duì)于某些與你差分版本一致,但是內(nèi)容有過修改的(比如破解版apk),這樣也是無(wú)法進(jìn)行增量升級(jí)的,為了防止合成補(bǔ)丁錯(cuò)誤,最好在補(bǔ)丁合成前對(duì)舊版本的apk進(jìn)行sha1sum校驗(yàn),保證基礎(chǔ)包的一致性。

    小實(shí)驗(yàn)

    多說無(wú)益,實(shí)踐才是王道。下面就來簡(jiǎn)單實(shí)踐一下,檢測(cè)之前理論的正確性。下載實(shí)驗(yàn)包?(http://download.csdn.net/detail/hmg25/4676737),解壓后文件如下 [html]?view plaincopyprint?
  • ├──?bsdiff-4.3????????//bsdiff的源碼路徑,官網(wǎng)獲取??
  • │???├──?bsdiff.1??
  • │???├──?bsdiff.c??
  • │???├──?bspatch.1??
  • │???├──?bspatch.c??
  • │???└──?Makefile??
  • ├──?bsdiff-4.3.tar.gz??????
  • ├──?bsdiff4.3-win32?????//windows?PC端的測(cè)試工具??
  • │???├──?Binary?diff.txt??
  • │???├──?bsdiff.exe??
  • │???├──?bspatch.exe??
  • │???└──?LICENSE??
  • ├──?bspatch?????????????//手機(jī)端的測(cè)試工具??
  • ├──?iReader1.6.2.0(v35).apk??????//?舊版本的apk??
  • └──?iReader1.8.0.1(v40).apk????//新版本的apk??

  • ? ? ? ?以附帶的iReader來做測(cè)試,在shell進(jìn)入test\bsdiff4.3-win32文件夾,并下運(yùn)行命令:
    [html]?view plaincopyprint?
  • bsdiff.exe???../iReader1.6.2.0(v35).apk???../iReader1.8.0.1(v40).apk???../ireader.patch??
  • ? ? ? ?原來的apk(2.94M),新版本的(3.24M),得到的patch文件為1.77M,用戶需要下載的就只是1.77M,流量節(jié)省了很多。
    ? ? ?下面先在電腦端將他們合并。
    ? ?? [html]?view plaincopyprint?
  • bspatch.exe??../iReader1.6.2.0(v35).apk???../new.apk????../ireader.patch??
  • ? ? ?執(zhí)行后得到名為new.apk 的合成版本應(yīng)用,我在ubuntu下進(jìn)行校驗(yàn)可以看出他們是一樣的。

    ? ? 下面我們?cè)谑謾C(jī)端合成看看,將根目錄下的bspatch(此為手機(jī)端運(yùn)行的)、iReader1.6.2.0(v35).apk 和ireader.patch ,通過adb push到你有權(quán)限操作的目錄,最好是在/sdcard/下,然后設(shè)置bspatch的執(zhí)行權(quán)限,重復(fù)操作上述命令,可以合成新版本的apk,稍后安裝查看驗(yàn)證版本即可,不詳述。

    擴(kuò)展閱讀

    使用bsdiff 進(jìn)行差分升級(jí),還并不是最優(yōu)的方式,google在它的Chromium項(xiàng)目中,對(duì)這個(gè)差分算法進(jìn)行了優(yōu)化,優(yōu)化后的版本叫做小胡瓜Courgette,據(jù)說性能優(yōu)化了很多不是一個(gè)數(shù)量級(jí)了,官方的一個(gè)例子:
    Full update ? ? ??10,385,920 bsdiff update? ? ?704,512 Courgette update?? ? ?78,848
    ? ? ? ?大牛們可以去研究下。
    ? ? ? 最近有些小忙,稍后有時(shí)間會(huì)對(duì)增量升級(jí)進(jìn)行封裝下,將合成的代碼弄成一個(gè)lib庫(kù),供java調(diào)用。有興趣的童鞋可以自己操作一下~~~ By 何明桂(http://blog.csdn.net/hmg25) 轉(zhuǎn)載請(qǐng)注明出處 ? 原裝正版,盜版必究 ^_^

    補(bǔ)充: 很多人不知道怎么進(jìn)行封裝為lib,其實(shí)這個(gè)和一般的android的C庫(kù)是一樣的,不明白的看看jni相關(guān)的知識(shí),原來的測(cè)試工程已經(jīng)找不到了,下邊我給出個(gè)簡(jiǎn)單的例子,拋磚引玉,給大家參考下。 ? [java]?view plaincopyprint?
  • #include?<stdio.h>??
  • #include?"com_hmg25_newstart_BSpatch.h"??
  • ??
  • #include?<bzlib.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;??
  • }??
  • ??
  • JNIEXPORT?jint?JNICALL?Java_com_hmg25_newstart_BSpatch_applyPatch(JNIEnv?*env,??
  • ????????jobject?obj,?jstring?old,?jstring?new?,?jstring?patch){??
  • ??int?argc=4;??
  • ??char?*?argv[argc];??
  • ??argv[0]="bspatch";??
  • ??argv[1]=(*env)->GetStringUTFChars(env,old,?0);??
  • ??argv[2]=(*env)->GetStringUTFChars(env,new,?0);??
  • ??argv[3]=(*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;??
  • }??
  • Android.mk: [java]?view plaincopyprint?
  • LOCAL_PATH:=?$(call?my-dir)??
  • include?$(CLEAR_VARS)??
  • ??
  • #?This?is?the?target?being?built.??
  • LOCAL_MODULE:=?libbspatch??
  • ??
  • ??
  • #?All?of?the?source?files?that?we?will?compile.??
  • LOCAL_SRC_FILES:=?\??
  • ??com_hmg25_newstart_BSpatch.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) ?
  • ----摘自何明桂(http://blog.csdn.net/hmg25

    轉(zhuǎn)載于:https://www.cnblogs.com/MrZz/p/4448236.html

    總結(jié)

    以上是生活随笔為你收集整理的【Android-功能】Android应用增量更新的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。