Android增量更新原理和實(shí)踐

demo地址
https://github.com/po1arbear/bsdiff-android

已驗(yàn)證過(guò)與tinker的兼容性,支持manifest修改牡直,支持activity新增纳决,如有其它風(fēng)險(xiǎn)和隱藏漏洞,歡迎告知 ^ ^

最近apk的更新有些頻繁饵史,各個(gè)系統(tǒng)發(fā)版都要向用戶推送一波更新约急,每次都要全量下載苗分,流量消耗大,用戶等待時(shí)間長(zhǎng)奴饮,打開(kāi)小米應(yīng)用商店择浊,發(fā)現(xiàn)大部分app更新包都比實(shí)際的包要小琢岩,于是研究了一下,發(fā)現(xiàn)是使用的增量更新担孔,了解了其原理并運(yùn)用到項(xiàng)目中實(shí)踐

一糕篇、 什么是增量更新?

首先需要明確挑豌,Android增量更新熱修復(fù)不同的技術(shù)概念。

熱修復(fù)一般是用于當(dāng)已經(jīng)發(fā)布的app有Bug需要修復(fù)的時(shí)候侯勉,開(kāi)發(fā)者修改代碼并發(fā)布補(bǔ)丁壳鹤,讓應(yīng)用能夠在不需要重新安裝的情況下實(shí)現(xiàn)更新饰迹,主流方案有Tinker啊鸭、AndFix等匿值。

而增量更新的目的是為了減少更新app所需要下載的包體積大小,常見(jiàn)如手機(jī)端游戲钟些,apk包體積為幾百M(fèi)政恍,但有時(shí)更新只需下載十幾M的安裝包即可完成更新达传。

二、增量更新原理

image.png

自從 Android 4.1 開(kāi)始宗弯, Google Play 引入了應(yīng)用程序的增量更新功能蒙保,App使用該升級(jí)方式,可節(jié)省約2/3的流量邓厕。

Smart app updates is a new feature of Google Play that introduces a better way of delivering app updates to devices. When developers publish an update, Google Play now delivers only the bits that have changed to devices, rather than the entire APK. This makes the updates much lighter-weight in most cases, so they are faster to download, save the device’s battery, and conserve bandwidth usage on users’ mobile data plan. On average, a smart app update is about 1/3 the sizeof a full APK update.

三邑狸、應(yīng)用市場(chǎng)現(xiàn)狀

筆者使用的小米手機(jī)涤妒,可以看到,小米的應(yīng)用商店已經(jīng)開(kāi)始支持增量更新屿储,會(huì)比原有的方式節(jié)省超過(guò)一半的流量

image.png

四渐逃、實(shí)現(xiàn)方案

  • 服務(wù)端

服務(wù)端的同學(xué)拿到客戶端同學(xué)開(kāi)發(fā)的新版本A茄菊,跟已發(fā)布的舊版本B1,B2竖哩,B3...做了差分生成相應(yīng)的差分包C1脊僚,C2辽幌,C3...,并生成相應(yīng)差分包的MD5值

  • 客戶端
  1. 客戶端用版本號(hào)作為參數(shù)向服務(wù)端請(qǐng)求更新數(shù)據(jù)虑润,若服務(wù)端沒(méi)有差分包或者差分包大小比全量包大時(shí)逛犹,則返回全量包下載URL虽画、MD5值

  2. 若服務(wù)端存在相應(yīng)的差分包則返回差分包下載URL,全量包和差分包MD5值渗柿,全量包簽名值和MD5值朵栖。把差分包下載到本地之后(C1)柴梆,先做MD5值校驗(yàn),確保下載的差分包數(shù)據(jù)的完整性门扇,校驗(yàn)失敗則走全量更新邏輯,校驗(yàn)無(wú)誤和本地現(xiàn)有安裝的舊版本(B1)進(jìn)行差分合并生成新版本(A)霸奕,之后進(jìn)行合成版本的MD5值校驗(yàn)和簽名校驗(yàn)质帅,確保合成文件的完整性和簽名信息的正確性留攒。校驗(yàn)無(wú)誤后再進(jìn)行安裝。

五盟庞、操作步驟

macOS操作驗(yàn)證
  1. 安裝
    brew install bsdiff
  2. 準(zhǔn)備oldfile和newfie
  3. 生成差量文件
    bsdiff oldfile newfile patchfile
  4. 合成新包
    bspatch oldfile newfile patchfile
Android上的實(shí)現(xiàn)

因?yàn)椴盍堪菑慕涌讷@取的,所以客戶端只需要處理bspatch的過(guò)程票彪,合成新的apk文件然后安裝即可

1.bsdiff下載

bsdiff下載后降铸,解壓bsdiff-4.3.tar.gz推掸,取出目錄中的bspatch.c文件,我們要用的就是這個(gè)文件中的bspatch_main方法登渣。

  1. bzip2下載
    取出文件blocksort.c胜茧,bzip2.c仇味,bzlib.c丹墨,bzlib.h贩挣,bzlib_private.h没酣,compress.c四康,crctable.c闪金,decompress.c论颅,huffman.c恃疯,randtable.c今妄,因?yàn)閎sdiff的編譯需要依賴bzip2,所以需要這些c文件犬性。

  2. 將bspatch.c以及bzip的相關(guān)代碼拷貝到j(luò)ni目錄下

image.png
  1. 編寫update-lib.cpp

    
    #include <jni.h>
    #include <string>
    #include <android/log.h>
    #include <exception>
    
    #include "patchUtils.h"
    extern "C"
    JNIEXPORT jint JNICALL
    Java_com_fcbox_hivebox_update_PatchUtils_patch(JNIEnv *env, jclass type, jstring oldApkPath_,
                                               jstring newApkPath_, jstring patchPath_) {
    
        int argc = 4;
        char *ch[argc];
        ch[0] = (char *) "bspatch";
        ch[1] = const_cast<char *>(env->GetStringUTFChars(oldApkPath_, 0));
        ch[2] = const_cast<char *>(env->GetStringUTFChars(newApkPath_, 0));
        ch[3] = const_cast<char *>(env->GetStringUTFChars(patchPath_, 0));
    
    
        int ret = applypatch(argc, ch);
        __android_log_print(ANDROID_LOG_INFO, "ApkPatchLibrary", "applypatch result = %d ", ret);
    
    
        env->ReleaseStringUTFChars(oldApkPath_, ch[1]);
        env->ReleaseStringUTFChars(newApkPath_, ch[2]);
        env->ReleaseStringUTFChars(patchPath_, ch[3]);
    
    
        return ret;
    }
    
  2. 編寫PatchUtils.java

public class PatchUtils {

    // Used to load the 'native-lib' library on application startup.
    static {
      System.loadLibrary("update-lib");
    }

    /**
     * native方法 使用路徑為oldApkPath的apk與路徑為patchPath的補(bǔ)丁包,合成新的apk鹤耍,并存儲(chǔ)于newApkPath
     *
     * 返回:0稿黄,說(shuō)明操作成功
     *
     * @param oldApkPath 示例:/sdcard/old.apk
     * @param outputApkPath 示例:/sdcard/output.apk
     * @param patchPath  示例:/sdcard/xx.patch
     * @return
     */
    public static native int patch(String oldApkPath, String outputApkPath,
        String patchPath);

  }
  1. 調(diào)用bspatch生成新的apk
 private void genNewApk() {
    String oldpath = getApplicationInfo().sourceDir;
    String newpath = (Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator
        + "composed_hivebox_apk.apk");

    String patchpath = (Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator
        + "bs_patch");
    PatchUtils.patch(oldpath, newpath, patchpath);

  }

五抛猖、與Tinker的差異

首先簡(jiǎn)單了解下Dex文件财著,大家在反編譯的時(shí)候撑碴,都清楚apk中會(huì)包含一個(gè)或者多個(gè)*.dex文件,該文件中存儲(chǔ)了我們編寫的代碼伟姐,一般情況下我們還會(huì)通過(guò)工具轉(zhuǎn)化為jar,然后通過(guò)一些工具反編譯查看愤兵。

jar文件大家應(yīng)該都清楚鹿霸,類似于class文件的壓縮包,一般情況下秆乳,我們直接解壓就可以看到一個(gè)個(gè)class文件懦鼠。而dex文件我們無(wú)法通過(guò)解壓獲取內(nèi)部的一個(gè)個(gè)class文件,說(shuō)明dex文件擁有自己特定的格式:

dex對(duì)JAVA類文件重新排列屹堰,將所有JAVA類文件中的常量池分解肛冶,消除其中的冗余信息,重新組合形成一個(gè)常量池扯键,所有的類文件共享同一個(gè)常量池睦袖,使得相同的字符串、常量在DEX文件中只出現(xiàn)一次荣刑,從而減小了文件的體積馅笙。

微信通過(guò)深入Dex格式延蟹,實(shí)現(xiàn)一套diff差異小斥杜,內(nèi)存占用少以及支持增刪改的算法

它格式無(wú)關(guān)忘渔,但對(duì)Dex效果不是特別好畦粮,當(dāng)前微信對(duì)于so與部分資源,依然使用bsdiff算法

image.png

核心思想:

  1. 將舊文件二進(jìn)制使用后綴排序或哈希算法形成一個(gè)字符串索引。
  2. 使用該字符串索引對(duì)比新文件钩蚊,生成差異文件(difference file)和新增文件(extra file)鸣驱。
  3. 將差異文件和新增文件及必要的索引控制信息壓縮為差異更新包踊东。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缎脾,一起剝皮案震驚了整個(gè)濱河市遗菠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贺拣,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異陨瘩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蒿囤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)建邓,“玉大人官边,你說(shuō)我怎么就攤上這事」羁剩” “怎么了妄辩?”我有些...
    開(kāi)封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵哮伟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)婶恼,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任荔泳,我火速辦了婚禮,結(jié)果婚禮上擎椰,老公的妹妹穿的比我還像新娘叹侄。我一直安慰自己贯底,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般臼闻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上乓搬,一...
    開(kāi)封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天江掩,我揣著相機(jī)與錄音,去河邊找鬼着降。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盅弛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了遂鹊?” 一聲冷哼從身側(cè)響起调限,我...
    開(kāi)封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤淘钟,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后妙色,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡定庵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年畴博,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杂曲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡噪漾,死狀恐怖疙剑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤摔笤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布寞冯,位于F島的核電站咆疗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏昧辽。R本人自食惡果不足惜咕痛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一构蹬、第九天 我趴在偏房一處隱蔽的房頂上張望悔据。 院中可真熱鬧绷雏,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)莫瞬。三九已至喂江,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工瘦麸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留了赌,地道東北人逢并。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓玻蝌,卻偏偏與公主長(zhǎng)得像许饿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 增量更新在Android開(kāi)發(fā)中是一種很常見(jiàn)的技術(shù)吏恭。 增量更新的原理 增量更新的原理非常簡(jiǎn)單搅幅,就是將本地apk與服務(wù)...
    re冷星閱讀 1,570評(píng)論 3 3
  • 首先增量更新應(yīng)該了解個(gè)概念:增量更新:在版本較近的apk升級(jí)的時(shí)候漾抬,根據(jù)兩個(gè)apk之間的差異(生成差異包),合成新...
    筆墨Android閱讀 1,065評(píng)論 3 8
  • 增量更新 概述 簡(jiǎn)單點(diǎn)來(lái)說(shuō)就是兩個(gè)文件:old和new,可通過(guò)差分工具生成它們的差分包patch叹括,當(dāng)我們需要時(shí)挖藏,再...
    DoubleD_譜閱讀 968評(píng)論 0 0
  • 上一節(jié)我們學(xué)習(xí)了NDK來(lái)處理文件的拆分和合并操作粱胜,那時(shí)候我們純手工來(lái)敲C語(yǔ)言的代碼,今天我們來(lái)用C語(yǔ)言代碼搞搞ND...
    Lypop閱讀 310評(píng)論 0 0
  • 一野哭、概述 增量更新相較于全量更新的好處不言而喻绰沥,利用差分算法獲得1.0版本到2.0版本的差分包,這樣在安裝了1.0...
    咸魚Jay閱讀 2,482評(píng)論 0 3