貨拉拉 Android H5離線包原理與實(shí)踐

點(diǎn)擊上方藍(lán)字關(guān)注我,知識(shí)會(huì)給你力量

背景
在實(shí)際業(yè)務(wù)中,app中的H5頁(yè)面使用的場(chǎng)景越來越多,在貨拉拉app中也存在大量的H5頁(yè)面,比如金秋拉貨節(jié)、余額、車型介紹頁(yè)等,加載速度成為了困擾用戶的一個(gè)痛點(diǎn)。為此我們決定引入離線包方案,另外還需要解決傳統(tǒng)離線包方案不靈活,體積大,不易管理,不易降級(jí)等問題,我們?cè)O(shè)計(jì)和開發(fā)一套H5離線包系統(tǒng),經(jīng)過幾個(gè)sdk版本的迭代,目前貨拉拉H5離線包sdk,已在多個(gè)業(yè)務(wù)中落地,接受了大量用戶檢驗(yàn)。車型介紹頁(yè)面使用離線包前后打開效果:


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

App端流程圖

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

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

2)三重降級(jí)策略
a.客戶端自動(dòng)降級(jí)。
本地沒有離線包時(shí),客戶端會(huì)自動(dòng)將啟用了離線包的H5頁(yè)面降級(jí)為線上H5頁(yè)面。
b.客戶端遠(yuǎn)程配置降級(jí)。
可以設(shè)置局部降級(jí),即臨時(shí)將某個(gè)使用離線包的H5頁(yè)面降級(jí)為線上,也可設(shè)置全局降級(jí),關(guān)閉所有頁(yè)面的離線包功能。接入方可以自行根據(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ù)端接口降級(jí)。
服務(wù)端提供的離線包查詢接口也可以設(shè)置將某個(gè)頁(yè)面降級(jí)為線上H5,也可以支持讓客戶端更新離線包后強(qiáng)制刷新。目前,強(qiáng)制刷新為空實(shí)現(xiàn),需要接入方自己實(shí)現(xiàn),例如重啟當(dāng)前頁(yè)面,關(guān)閉當(dāng)前頁(yè)面等。
降級(jí)策略流程圖如下:

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


此外離線包sdk還有離線包下載,請(qǐng)求,解壓的耗時(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頁(yè)面加載成功率也有提升。頁(yè)面主框架(不考慮動(dòng)態(tài)數(shù)據(jù))加載成功率從96%提升到100%。


后期工作與展望
擴(kuò)大開源范圍。比如支持?jǐn)帱c(diǎn)續(xù)傳的下載SDK,后續(xù)會(huì)考慮開源。離線包依賴的后端服務(wù)暫時(shí)未開源,目前采取是通過HttpServer搭建一個(gè)簡(jiǎn)單的本地Web Server,可保證離線包示例在本地正常運(yùn)行。
具體使用方法參考開源代碼中介紹(https://github.com/HuolalaTech/HLLOfflineWebView-android )
參考資料
https://zhuanlan.zhihu.com/p/34125968
https://juejin.cn/post/6844903934004297736
作者介紹
貨拉拉移動(dòng)端技術(shù)團(tuán)隊(duì)

向大家推薦下我的網(wǎng)站 https://xuyisheng.top/ 點(diǎn)擊原文一鍵直達(dá)
專注 Android-Kotlin-Flutter 歡迎大家訪問
往期推薦
更文不易,點(diǎn)個(gè)“三連”支持一下??
