貨拉拉 Android H5離線包原理與實(shí)踐!
大家好,我是皇叔,最近開了一個(gè)安卓進(jìn)階漲薪訓(xùn)練營,可以幫助大家突破技術(shù)&職場瓶頸,從而度過難關(guān),進(jìn)入心儀的公司。
詳情見文章:沒錯(cuò)!皇叔開了個(gè)訓(xùn)練營
背景
在實(shí)際業(yè)務(wù)中,app中的H5頁面使用的場景越來越多,在貨拉拉app中也存在大量的H5頁面,比如金秋拉貨節(jié)、余額、車型介紹頁等,加載速度成為了困擾用戶的一個(gè)痛點(diǎn)。為此我們決定引入離線包方案,另外還需要解決傳統(tǒng)離線包方案不靈活,體積大,不易管理,不易降級等問題,我們設(shè)計(jì)和開發(fā)一套H5離線包系統(tǒng),經(jīng)過幾個(gè)sdk版本的迭代,目前貨拉拉H5離線包sdk,已在多個(gè)業(yè)務(wù)中落地,接受了大量用戶檢驗(yàn)。車型介紹頁面使用離線包前后打開效果:


行業(yè)方案
目前H5離線包方案,通常是將離線包置入assets目錄中,打包在apk內(nèi)部,用戶使用過程中再按需加載。所以大部分情況下可能存在以下問題:
由于離線包內(nèi)容固定導(dǎo)致更新不及時(shí) 當(dāng)離線包內(nèi)容較多或者離線包個(gè)數(shù)較多時(shí),會嚴(yán)重影響App包體積 由于離線包內(nèi)部的邏輯固定,當(dāng)出現(xiàn)問題時(shí)無法降級,無法禁用 上線沒有數(shù)據(jù)對比無法知道上線效果
針對以上痛點(diǎn),我們團(tuán)隊(duì)對離線包進(jìn)行設(shè)計(jì)優(yōu)化,應(yīng)用于團(tuán)隊(duì)內(nèi)的多個(gè)應(yīng)用,多個(gè)業(yè)務(wù)場景中。
技術(shù)實(shí)現(xiàn)
H5離線包的基本原理是將html、js、css、圖片等靜態(tài)資源打包到成壓縮文件,然后下載到客戶端,H5加載時(shí)靜態(tài)資源直接從本地取文件,減少網(wǎng)絡(luò)請求,提高速度。加載本地文件路徑存在的問題和解決:
| 存在問題 | 解決方法 |
|---|---|
| cgi請求跨域 | 跨域請求頭增加null支持 |
| cookie跨域問題 | 目前靜態(tài)js中無cookie操作,沒有cookie跨域問題 |
| localstorage跨域問題 | 暫時(shí)不涉及域名隔離問題,如果有需要,采取調(diào)用原生的方式解決 |
| 前端使用絕對路徑問題 | 相對路徑 |
4.1 總體結(jié)構(gòu)
H5發(fā)布基本流程

App端流程圖

前端的打包平臺,支持發(fā)布為線上頁面,也支持發(fā)布為離線包。離線包模式時(shí),客戶端會先查詢是否有離線包需要更新,有則更新,同時(shí)支持離線包降級為線上網(wǎng)頁。
H5離線包和線上H5一樣也能進(jìn)行更新和升級,有三個(gè)更新時(shí)機(jī):
1)WebView容器打開時(shí)更新。在需要開啟離線包功能的H5頁面打開時(shí),會去后端檢查對應(yīng)的離線包頁面是否有更新。如果有更新,則下載離線包到本地,絕大部分場景是下次打開時(shí)生效。
2)啟動查詢離線包更新。對于實(shí)時(shí)性要求比較高的頁面,可配置在啟動時(shí)檢查更新。
3)通過長連接推送的方式通知客戶端下載最新的離線包。(需要接入方自己實(shí)現(xiàn)長鏈接,調(diào)用SDK更新方法)
4.2 性能優(yōu)化
1)多業(yè)務(wù)并行化,單業(yè)務(wù)串行
離線包檢查更新時(shí),存在同時(shí)查詢多個(gè)業(yè)務(wù)的離線包是否有更新的情況,為了提高查詢效率,多個(gè)業(yè)務(wù)離線包檢查的請求采取并行請求的方式??紤]到后端改造成本問題,目前還不支持聚合查詢,計(jì)劃在后續(xù)版本中完善。另外,考慮業(yè)務(wù)流程的更新流程取消可能導(dǎo)致不穩(wěn)定,單業(yè)務(wù)只做串行,避免過程中文件損壞,下載不全,線程并發(fā)的問題。

2)啟動預(yù)下載
大部分離線包查詢和下載的時(shí)機(jī)為打開H5頁面時(shí),由于離線包查詢、下載、解壓總體耗時(shí)較長,導(dǎo)致首次打開無法命中離線包。所以貨拉拉離線包支持配置部分離線包在啟動時(shí)檢查和下載離線包。配置為:
OfflineConfig offlineConfig = new OfflineConfig.Builder(true)
.addPreDownload("offline-pkg-name")//預(yù)加載業(yè)務(wù)名稱
.build();,
4.3 可靠性設(shè)計(jì)
1)解壓操作可靠性設(shè)計(jì)
文件解壓耗時(shí)較長(大約30ms),如果中間程序退出可能會導(dǎo)致只解壓了其中一半文件,影響后續(xù)離線包邏輯。所以解壓到文件夾操作采取先解壓,然后重命名,保證最后的文件夾的里的文件是完整的。同時(shí)當(dāng)離線包正在使用時(shí),一般情況下采取先解壓,下次生效的策略,極端情況下可以立刻生效,但會導(dǎo)致頁面強(qiáng)刷,影響用戶體驗(yàn)。操作過程采取了temp、new、cur三個(gè)文件夾,解壓細(xì)節(jié)如下

2)三重降級策略
a.客戶端自動降級。
本地沒有離線包時(shí),客戶端會自動將啟用了離線包的H5頁面降級為線上H5頁面。
b.客戶端遠(yuǎn)程配置降級。
可以設(shè)置局部降級,即臨時(shí)將某個(gè)使用離線包的H5頁面降級為線上,也可設(shè)置全局降級,關(guān)閉所有頁面的離線包功能。接入方可以自行根據(jù)自己服務(wù)端下發(fā)參數(shù)進(jìn)行配置:
OfflineConfig offlineConfig = new OfflineConfig.Builder(true)//總開關(guān)
.addDisable("disable-offline-pkg-name")//禁用業(yè)務(wù)名稱
.addPreDownload("offline-pkg-name")//預(yù)加載業(yè)務(wù)名稱
.build();
c.服務(wù)端接口降級。
服務(wù)端提供的離線包查詢接口也可以設(shè)置將某個(gè)頁面降級為線上H5,也可以支持讓客戶端更新離線包后強(qiáng)制刷新。目前,強(qiáng)制刷新為空實(shí)現(xiàn),需要接入方自己實(shí)現(xiàn),例如重啟當(dāng)前頁面,關(guān)閉當(dāng)前頁面等。
降級策略流程圖如下:

3)性能監(jiān)控
貨拉拉對webview的加載成功率,錯(cuò)誤碼、耗時(shí)進(jìn)行了統(tǒng)計(jì)上報(bào),通過監(jiān)控面板查看。


此外離線包sdk還有離線包下載,請求,解壓的耗時(shí)、結(jié)果數(shù)據(jù)上報(bào)。監(jiān)控和上報(bào)采取的接口擴(kuò)展方式,接入方根據(jù)業(yè)務(wù)特點(diǎn)選用具體的數(shù)據(jù)上報(bào)sdk。
4.4 效能優(yōu)化
離線包和URL映射配置化

配置格式如下:主要通過url中的host、path、Fragment配置命中規(guī)則。根據(jù)接入方是否需要傳入,不需要可以不傳遞。
//匹配規(guī)則相關(guān) 可選
ArrayList<String> host = new ArrayList<>();
ArrayList<String> path = new ArrayList<>();
ArrayList<String> fragment = new ArrayList<>();
host.add("www.xxxx.cn");
path.add("/aaa");
fragment.add("/ccc=ddd");
OfflineRuleConfig offlineRuleConfig = new OfflineRuleConfig();
offlineRuleConfig.addRule(new OfflineRuleConfig.RulesInfo("offline-pkg-name",host,path,fragment));
new OfflineParams()
.addRule("offline-pkg-name",host,path,fragment)//自定義配置的形式
.setRule(Constants.RULE_CONFIG)//json形式的規(guī)則
.setRule(offlineRuleConfig)//實(shí)體類形式
{
"rules": [{
"host": ["test1.xxx.cn", "test2.xxx.cn"],
"path": ["/pathA"],
"offweb": "offline-pkg-name-a"
},
{
"host": ["www.aaa.cn", "aaa.xxxx.cn"],
"path": ["aaa/path", "bbb/path"],
"offweb": "offline-pkg-name-b"
}
]
}
總結(jié)
離線包上線后,收益明顯,平均加載速度從2秒提升到1秒,同時(shí)H5頁面加載成功率也有提升。頁面主框架(不考慮動態(tài)數(shù)據(jù))加載成功率從96%提升到100%。


后期工作與展望
擴(kuò)大開源范圍。比如支持?jǐn)帱c(diǎn)續(xù)傳的下載SDK,后續(xù)會考慮開源。離線包依賴的后端服務(wù)暫時(shí)未開源,目前采取是通過HttpServer搭建一個(gè)簡單的本地Web Server,可保證離線包示例在本地正常運(yùn)行。
具體使用方法參考開源代碼中介紹(https://github.com/HuolalaTech/HLLOfflineWebView-android )
參考資料
https://zhuanlan.zhihu.com/p/34125968
https://juejin.cn/post/6844903934004297736
作者介紹
貨拉拉移動端技術(shù)團(tuán)隊(duì)
為了防止失聯(lián),歡迎關(guān)注我防備的小號
微信改了推送機(jī)制,真愛請星標(biāo)本公號??



