Android應用增量升級方案之實踐篇
作者: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使用者和土豪使用者)
以北京移動為例,最基礎套餐,5元30M流量。而最新的微信APK安裝包35M。也就是說,如果你選了最基礎套餐,你一個月內使用全量升級方案,升級一次微信,流量都不夠用。
2.在敏捷開發大行其道的今天,開發者希望儘快將新開發的功能推送到使用者面前,並及時得到使用者的反饋。恨不能三天一小版,一週一大版。
綜合以上因素,開發者必須為使用者考慮:省流量,省流量,省流量。
顯然,全量升級這種土豪做法已經不再適用,於是,增量升級應運而生。
增量升級原理
首先,兩句話簡單概括增量升級原理:
1.服務端通過比對最新升級包,和當前應用包,生成差分升級包;
2.客戶端將差分升級包和當前應用包合併,生成最新升級包。
接下來,簡單介紹下增量升級的原理:
1.首先,客戶端獲取當前應用的Version Code和應用APK檔案的MD5值,傳送給伺服器;
2.伺服器根據既定策略,給使用者返回更新包資訊。
- 通過MD5值沒有查詢到舊有APK應用資訊,返回全量升級包網址,全量升級包MD5值;
- 需要返回patch包,但還沒有生成patch包時,後臺去生成patch包,並返回全量升級包網址,全量升級包MD5值;
- 需要返回patch包,並且已經生成patch包時,返回patch包網址,patch包MD5值,全量升級包網址,全量升級包MD5值;
- 不需要返回patch包,則返回全量升級包網址,全量升級包MD5值;
3.客戶端根據返回資訊進行更新操作。
- 如果只有全量升級包相關資訊,則下載全量升級包,並在校驗MD5值後,安裝更新包;
- 如果有差分升級包(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
- 在build.gradle檔案中自定義jnilib目錄
sourceSets {
main {
jniLibs.srcDirs = [`libs`]
}
}
- 將
app/libs/armeabi-v7a/libbsdiffpatch.so
拷貝到你的工程對應目錄下。 - 將
app/src/main/java/com/github/snowdream/bsdiffpatch
和app/src/main/java/com/github/snowdream/diffpatch
拷貝到你的工程對應目錄下,包名和檔名都不能改變。 - 在Java檔案中參考以下程式碼進行呼叫。
IDiffPatch bsDiffPatch = new BSDiffPatch();
bsDiffPatch.init(getApplicationContext());
//diff
bsDiffPatch.diff(oldFilePath, newFilePath, diffFilePath);
//patch
bsDiffPatch.patch(oldFilePath, diffFilePath, gennewFilePath);
HDiffPatch
- 在build.gradle檔案中自定義jnilib目錄
sourceSets {
main {
jniLibs.srcDirs = [`libs`]
}
}
- 將
app/libs/armeabi-v7a/libhdiffpatch.so
拷貝到你的工程對應目錄下。 - 將
app/src/main/java/com/github/snowdream/hdiffpatch
和app/src/main/java/com/github/snowdream/diffpatch
拷貝到你的工程對應目錄下,包名和檔名都不能改變。 - 在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
-
測試結果:(詳見下圖)
- BsDiffPatch生成的patch包稍小。
- 兩者的diff操作都非常耗資源,耗時間,無法忍受。(當然diff操作一般在服務端進行)
- 兩者的patch操作都比較快。通過大概五次測試,BsDiffPatch的patch操作需要13s左右,而HDiffPatch的patch操作僅僅需要1s左右。
以上結果僅供參考。
-
測試結論:
- BsDiffPatch應用更廣泛
- HDiffPatch看起來更高效
擴充套件
以上算是比較成熟的增量升級方案了,但是仔細想想,可能還存在一些問題:
1.由於多渠道,多版本造成非常多Patch包
2.bs diff/patch演算法效能和記憶體開銷太高
第一個問題可以通過伺服器策略進行限制。比如,只有最新版5個版本內的升級採用增量升級,其他的仍然採用全量升級。
據說,還有一種更強大的演算法,可以解決以上問題。大家有興趣的話,可以自己去了解。
crsync-基於rsync rolling演算法的檔案增量更新.md
參考
- 友盟增量更新的原理是什麼
- Android應用增量更新庫(Smart App Updates)
- Android實現應用的增量更新升級
- https://github.com/smuyyh/IncrementallyUpdate
- 淺析android應用增量升級
- https://github.com/cundong/SmartAppUpdates
- crsync-基於rsync rolling演算法的檔案增量更新.md
- https://github.com/sisong/HDiffPatch
- http://www.daemonology.net/bsdiff/
- https://github.com/snowdream/android-diffpatch
相關文章
- Android應用增量升級Android
- Android實現應用的增量更新\升級Android
- Android不使用第三方升級庫實現應用升級Android
- Android快應用實踐Android
- NGINX 入門到企業級應用實踐-基礎篇Nginx
- React 應用實踐(基礎篇)React
- AWS RDS強制升級的應對之道——版本升級的最佳實踐
- WAS JDK升級實施方案JDK
- Android應用保活實踐Android
- Android 中的升級資料庫最佳方法實踐Android資料庫
- 升級Webpack5實踐Web
- MySQL 升級的最佳實踐MySql
- Kubernetes 實戰——升級應用(Deployment)
- Flutter專案之app升級方案FlutterAPP
- MySQL 之XtraBackup全量增量熱備實踐MySql
- 最佳實踐 | 原始碼升級gcc原始碼GC
- Jenkins 使用指南 之 初級應用篇Jenkins
- 深度解讀!阿里統一應用管理架構升級的教訓與實踐阿里架構
- FFmpeg應用實踐之命令查詢
- JavaFX桌面應用-版本升級Java
- kubernetes實踐之四十:Pod的升級與回滾
- 愛奇藝iOS深度實踐 | SiriKit詳解應用篇iOS
- Android技術分享| Bugly 應用升級自定義UIAndroidUI
- PostgreSQL升級方案SQL
- 使用 tableflip 實現應用的優雅熱升級
- 使用Bugly應用升級SDK實現App更新APP
- 得物質量度量之“三級指標體系”及其應用實踐指標
- Android面試之Java中級篇Android面試Java
- Android應用加固的簡單實現方案Android
- vivo商城前端架構升級—多端統一探索、實踐與展望篇前端架構
- Util 應用框架 UI 全新升級框架UI
- 重新整理 .net core 實踐篇————配置應用[一]
- 多頁應用增量更新靜態資源Webpack打包方案Web
- HDFS3.2升級在滴滴的實踐S3
- Android應用優化方案Android優化
- Android應用架構的發展和實踐Android應用架構
- flutter跨平臺開發之App升級方案FlutterAPP
- RestCloud ETL實踐之無標識位實現增量資料同步RESTCloud