Android應用增量升級方案之實踐篇

snowdream86發表於2016-08-24

作者:snowdream
Email:yanghui1986527#gmail.com
QQ 群: 529327615
原文地址:https://snowdream.github.io/blog/2016/08/23/android-incremental-update-solutions/

名詞解釋

全量升級

每次下載完整的新安裝包,進行覆蓋安裝。

增量升級

將新安裝包和已經安裝的舊安裝包進行比對,生成一個差分升級包(Patch包)。使用者下載patch包後,和已經安裝的舊安裝包進行合併,生成新安裝包,再進行覆蓋安裝。

背景

在早期的Android應用開發中,由於android應用普遍比較小,因此,普遍採用了全量升級方案。簡單粗暴,卻行之有效。
但是,隨著Android的發展,Android應用功能越來越多,體積越來越大,再綜合以下幾個因素考慮,全量升級方案逐漸無法滿足我們的需求。
1.在國內,隨著2G,3G,4G的逐步演進,手機網路越來越快,但有一點事實仍然沒有改變:流量很貴,非常不夠用。(這個因素不適合WIFI使用者和土豪使用者)
beijingmobile
以北京移動為例,最基礎套餐,5元30M流量。而最新的微信APK安裝包35M。也就是說,如果你選了最基礎套餐,你一個月內使用全量升級方案,升級一次微信,流量都不夠用。
2.在敏捷開發大行其道的今天,開發者希望儘快將新開發的功能推送到使用者面前,並及時得到使用者的反饋。恨不能三天一小版,一週一大版。

綜合以上因素,開發者必須為使用者考慮:省流量,省流量,省流量。

顯然,全量升級這種土豪做法已經不再適用,於是,增量升級應運而生。

增量升級原理

首先,兩句話簡單概括增量升級原理:
1.服務端通過比對最新升級包,和當前應用包,生成差分升級包;
2.客戶端將差分升級包和當前應用包合併,生成最新升級包。

接下來,簡單介紹下增量升級的原理:
1.首先,客戶端獲取當前應用的Version Code和應用APK檔案的MD5值,傳送給伺服器;
2.伺服器根據既定策略,給使用者返回更新包資訊。

  1. 通過MD5值沒有查詢到舊有APK應用資訊,返回全量升級包網址,全量升級包MD5值;
  2. 需要返回patch包,但還沒有生成patch包時,後臺去生成patch包,並返回全量升級包網址,全量升級包MD5值;
  3. 需要返回patch包,並且已經生成patch包時,返回patch包網址,patch包MD5值,全量升級包網址,全量升級包MD5值;
  4. 不需要返回patch包,則返回全量升級包網址,全量升級包MD5值;

3.客戶端根據返回資訊進行更新操作。

  1. 如果只有全量升級包相關資訊,則下載全量升級包,並在校驗MD5值後,安裝更新包;
  2. 如果有差分升級包(patch包),則下載差分升級包。校驗差分升級包的MD5值。如果校驗失敗,走上面一個步驟。如果校驗成功,則將差分升級包和當前版本的APK進行合併操作,生成新的應用包。校驗新的應用包MD5值。校驗通過,這安裝這個生成的新應用包。如果校驗失敗,則走上面一個步驟。

以上只是簡單介紹了增量升級的原理,實際應用中還需要細化,考慮更多的場景。

注意: 下載過程中,必須支援斷點續傳策略。防止網路不暢時,不斷重試,造成的流量浪費。

增量升級方案

增量升級方案的核心就是使用Diff/Patch演算法來對新舊APK進行diff/patch操作。
目前主流的Diff/Patch演算法是bsdiff/bspatch,來自:http://www.daemonology.net/bsdiff/

另外,我瞭解到,國內有人開源了另外一種Diff/Patch演算法,名字叫做HDiffPatch,來自:https://github.com/sisong/HDiffPatch

據說,比bsdiff/bspatch更高效呢?詳見《HDiff1.0和BSDiff4.3的對比測試》

我將bsdiff/bspatch和HDiffPatch,使用jni封裝成so庫,供android呼叫。專案地址: https://github.com/snowdream/android-diffpatch
在封裝HDiffPatch過程中遇到問題,得到作者@sisong的支援和幫助,在此表示感謝。

bsdiff/bspatch和HDiffPatch演算法都是開源的,服務端可以根據原始檔來進行編譯整合。
這裡我主要在Android客戶端的角度,介紹下bsdiff/bspatch和HDiffPatch怎麼使用。

BsDiffPatch

  1. 在build.gradle檔案中自定義jnilib目錄
sourceSets {
    main {
        jniLibs.srcDirs = [`libs`]
    }
}
  1. app/libs/armeabi-v7a/libbsdiffpatch.so 拷貝到你的工程對應目錄下。
  2. app/src/main/java/com/github/snowdream/bsdiffpatchapp/src/main/java/com/github/snowdream/diffpatch 拷貝到你的工程對應目錄下,包名和檔名都不能改變。
  3. 在Java檔案中參考以下程式碼進行呼叫。
IDiffPatch bsDiffPatch = new BSDiffPatch();
bsDiffPatch.init(getApplicationContext()); 
//diff
bsDiffPatch.diff(oldFilePath, newFilePath, diffFilePath);
//patch
bsDiffPatch.patch(oldFilePath, diffFilePath, gennewFilePath);

HDiffPatch

  1. 在build.gradle檔案中自定義jnilib目錄
sourceSets {
    main {
        jniLibs.srcDirs = [`libs`]
    }
}
  1. app/libs/armeabi-v7a/libhdiffpatch.so 拷貝到你的工程對應目錄下。
  2. app/src/main/java/com/github/snowdream/hdiffpatchapp/src/main/java/com/github/snowdream/diffpatch 拷貝到你的工程對應目錄下,包名和檔名都不能改變。
  3. 在Java檔案中參考以下程式碼進行呼叫。
IDiffPatch hDiffPatch = new HDiffPatch();
hDiffPatch.init(getApplicationContext()); 
//diff
hDiffPatch.diff(oldFilePath, newFilePath, diffFilePath);
//patch
hDiffPatch.patch(oldFilePath, diffFilePath, gennewFilePath);

BsDiffPatch vs HDiffPatch

這裡我選擇高德地圖Android客戶端的兩個版本來進行測試。

  • 測試來源:http://www.autonavi.com/
  • 測試版本: Amap_Android_V7.7.4.2128_GuanWang.apk 和 Amap_Android_V7.3.0.2036_GuanWang.apk (注:版本跨度大,差異大)
  • 對比演算法: BsDiffPatch vs HDiffPatch
  • 測試結果:(詳見下圖)

    1. BsDiffPatch生成的patch包稍小。
    2. 兩者的diff操作都非常耗資源,耗時間,無法忍受。(當然diff操作一般在服務端進行)
    3. 兩者的patch操作都比較快。通過大概五次測試,BsDiffPatch的patch操作需要13s左右,而HDiffPatch的patch操作僅僅需要1s左右。

以上結果僅供參考。

  • 測試結論:

    1. BsDiffPatch應用更廣泛
    2. HDiffPatch看起來更高效

test

擴充套件

以上算是比較成熟的增量升級方案了,但是仔細想想,可能還存在一些問題:
1.由於多渠道,多版本造成非常多Patch包
2.bs diff/patch演算法效能和記憶體開銷太高
第一個問題可以通過伺服器策略進行限制。比如,只有最新版5個版本內的升級採用增量升級,其他的仍然採用全量升級。
據說,還有一種更強大的演算法,可以解決以上問題。大家有興趣的話,可以自己去了解。
crsync-基於rsync rolling演算法的檔案增量更新.md

參考

  1. 友盟增量更新的原理是什麼
  2. Android應用增量更新庫(Smart App Updates)
  3. Android實現應用的增量更新升級
  4. https://github.com/smuyyh/IncrementallyUpdate
  5. 淺析android應用增量升級
  6. https://github.com/cundong/SmartAppUpdates
  7. crsync-基於rsync rolling演算法的檔案增量更新.md
  8. https://github.com/sisong/HDiffPatch
  9. http://www.daemonology.net/bsdiff/
  10. https://github.com/snowdream/android-diffpatch


相關文章