目錄
1肖揣、Hybrid整體結(jié)構(gòu)
2拭嫁、橋接層
3可免、基礎(chǔ)通信層
4、打開離線插件的流程
5做粤、離線插件數(shù)據(jù)預(yù)加載優(yōu)化
1浇借、Hybrid層次結(jié)構(gòu)
(1)H5頁面層。
(2)橋接層:BridgeJs是一個(gè).js文件怕品,是NA和H5通信的橋梁妇垢,WebView在加載url之前需要將BridgeJs前置注入。
(3)基礎(chǔ)通信層:該層主要由BridgeWebView堵泽、BridgeManager修己、WebPlugin組成,BridgeWebView提供了基本的頁面加載迎罗,并捕獲BridgeJs發(fā)送過來的事件交給BridgeManager進(jìn)行處理睬愤;BridgeManager具備BridgeWebView的控制能力,負(fù)責(zé)處理NA向H5以及H5向NA的消息處理纹安;WebPlugin是離線化插件尤辱,為了加速H5頁面的展示,可將某個(gè)業(yè)務(wù)的h5厢岂、css光督、js、圖片等資源打包塔粒,并離線化至本地结借,在打開相應(yīng)頁面的時(shí)候只需獲取其對應(yīng)頁面的數(shù)據(jù),省去h5卒茬、css船老、js咖熟、圖片等資源的下載時(shí)間。
(4)協(xié)議分發(fā)層:該層是一個(gè)總體的demo協(xié)議分發(fā)器柳畔,將收到的協(xié)議分發(fā)到各個(gè)協(xié)議實(shí)現(xiàn)層進(jìn)行處理馍管。
(5)協(xié)議實(shí)現(xiàn)層:該層針對demo協(xié)議不同的scheme,分別對應(yīng)不同的實(shí)現(xiàn)薪韩。NativePageCall用于APP內(nèi)各個(gè)頁面的跳轉(zhuǎn)确沸;WebSDKCall是用于為H5提供各種NA能力,WebPluginCall用于打開本地離線化插件俘陷。
(6)Native層:該層為客戶端App層罗捎,為上述幾種協(xié)議提供能力調(diào)用和支持。
2岭洲、橋接層
2.1宛逗、BridgeJs是什么坎匿?
BridgeJs是一個(gè).js文件盾剩,是NA和H5通信的橋梁,(作用是做隔離)替蔬。具體來說就是H5調(diào)用NA能力或者打開NA頁面必須通過調(diào)用BridgeJs中的方法來通知NA告私,NA也必須調(diào)用BridgeJs中的方法來給H5頁面發(fā)送消息。
2.2承桥、BridgeJs的注入方式
WebView注入BridgeJs文件的方式為驻粟,先將該文件讀入內(nèi)存作為BridgeJs,Android4.4以前通過loadUrl("javascript:" + BridgeJs)進(jìn)行注入凶异,BridgeJS注入完畢后蜀撑,在JS函數(shù)尾部通過onConsoleMessage向NA發(fā)起通知,標(biāo)記注入完畢的事件剩彬;4.4后的版本酷麦,可使用evaluateJavascript (String script, ValueCallback<String> resultCallback)方法注入并實(shí)現(xiàn)回調(diào),注入完成后可向?qū)?yīng)H5頁面種入NA基本信息喉恋,供H5使用并調(diào)用H5中Ready方法觸發(fā)H5頁面渲染沃饶。
3、 基礎(chǔ)通信層
3.1轻黑、BridgeWebView
由WebView+TitleBar+ProgressBar構(gòu)成糊肤,ProgressBar根據(jù)onProgressChanged(WebView view, int newProgress)顯示當(dāng)前頁面加載進(jìn)度,避免頁面加載時(shí)無狀態(tài)氓鄙,TitleBar支持多種主題馆揉,為H5提供三種UI操作元素(返回按鈕,標(biāo)題抖拦,功能按鈕)升酣;WebView是作為H5頁面的容器勤讽,在加載頁面的同時(shí),也負(fù)責(zé)捕獲頁面消息和注入JS拗踢。
3.2脚牍、BridgeManager
通過重寫shouldOverrideUrlLoading(WebView view, String url),將收到的重定向url交給BridgeManager去攔截巢墅,BridgeManager通過調(diào)用SchemeDispatcher. onDispatch(String url)的去處理消息诸狭;如果BridgeWebView主動向H5發(fā)送消息,則通過BridgeManager執(zhí)行相應(yīng)的javascript方法君纫。
3.3驯遇、 WebPlugin
3.3.1、 WebPlugin的作用
WebPlugin是為了加速H5頁面的展示速度蓄髓,我們打開一個(gè)h5頁面的流程大致如下
離線化插件的大致思路是將H5頁面中的html叉庐、js、css以及一些靜態(tài)的圖片資源打包離線化至本地会喝,在打開相應(yīng)頁面的時(shí)候從本地去打開陡叠,然后只需獲取對應(yīng)頁面的數(shù)據(jù),省去html肢执、js枉阵、圖片等資源的下載時(shí)間。
3.3.2预茄、 WebPlugin構(gòu)成
一個(gè)H5離線化插件一般對應(yīng)某個(gè)業(yè)務(wù)兴溜。
離線化插件一般對應(yīng)某個(gè)業(yè)務(wù),這個(gè)業(yè)務(wù)通常包括多個(gè)H5頁面耻陕。
離線化插件的內(nèi)容主要包括兩個(gè)方面:
1拙徽、多個(gè)H5頁面中的html、js诗宣、css以及一些靜態(tài)的圖片資源
2膘怕、config.json文件。
config.json文件是插件配置文件梧田,描述了這個(gè)插件中頁面的名稱和路徑的映射關(guān)系淳蔼,例如:
3.3.3、離線插件的工作流程
(1)裁眯、運(yùn)營平臺上傳打包好的離線插件鹉梨;
(2)、server端發(fā)下離線插件配置穿稳;
(3)存皂、App端根據(jù)server下發(fā)配置下載和更新離線插件。
(3.1)、server端下發(fā)離線插件配置包括一個(gè)allMd5值和一個(gè)plugin_list插件列表旦袋,外層的md5表示所有插件zip包共同計(jì)算的總md5值骤菠,用于和本地總md5對比,判斷是否有插件要更新疤孕。plugin_list中每個(gè)插件包含plugin_id商乎,url和md5,plugin_id唯一標(biāo)識該插件祭阀,并作為data/data/package name/Plugins/目錄下插件的路徑名鹉戚,url為插件下載地址,md5為插件zip包的md5值专控,用于判斷當(dāng)前插件是否需要更新及下載完成后校驗(yàn)該包的完整性抹凳。
離線插件分為正常更新和緊急更新:
3.3.4離線插件的正常更新
(1)首先檢測當(dāng)前是否有更新任務(wù)隊(duì)列正在運(yùn)行,如果有伦腐,則直接返回赢底;
(2)檢測接口返回的allMD5值與本地sharedPreferences記錄的上次成功更新的總md5值是否相同,如果相同柏蘑,則直接返回幸冻,否則繼續(xù);
(3)為每個(gè)plugin配置更新任務(wù)PluginUpdateTask辩越,然后放到線程池中執(zhí)行嘁扼,且所有的Task及其狀態(tài)被記錄在一個(gè)HashMap<String, PluginUpdateState>中,用于判斷是否所有的插件都更新完成并且成功黔攒;
(4)PluginUpdateTask實(shí)現(xiàn)單個(gè)插件的更新流程:先檢查本地相同pluginId的md5是否與新的相同,若相同强缘,則直接返回True督惰,否則,依次執(zhí)行插件下載旅掂、md5完整性校驗(yàn)赏胚、刪除舊插件、解壓商虐,最后將該插件新的md5值記錄到本地觉阅。(每個(gè)插件下載成功或者失敗都將其狀態(tài)記錄到HashMap<String, PluginUpdateState>中);
(5)檢測是否所有的插件更新完成并且成功秘车,如果是典勇,則將allMd5值記錄到本地,否則等待下次更新或者緊急更新叮趴。
3.3.5割笙、離線包緊急更新
Plugin緊急更新用于一些極端case,在某些場景下眯亦,用戶點(diǎn)擊進(jìn)入離線插件伤溉,可能會遭遇打開失敯懵搿(例如插件被清理,上一次更新異常等)乱顾,這種情況下板祝,需要根據(jù)插件的pluginId,為該plugin開啟獨(dú)立的插件下載任務(wù)走净,且不與正常的更新檢測任務(wù)耦合扔字。流程如下:
(1)插件啟動失敗,進(jìn)入BridgeWebView頁温技,顯示loading革为;
(2)根據(jù)pluginId,查找內(nèi)存中的PluginBean舵鳞,若找到震檩,則直接開始下載,若未找到蜓堕,則重新拉取離線插件接口抛虏,獲取PluginBean,
(3)為PluginBean配置PluginEmergencyTask套才,放到線程池中開啟緊急下載任務(wù)迂猴,復(fù)用單個(gè)插件下載邏輯下載該插件。(不同的是此時(shí)不需要對該plugin新的md5值和本地md5只校驗(yàn)背伴,因?yàn)槿绻窃摬寮G闆r下下載成功沸毁,但是被刪除,校驗(yàn)md5值會直接返回傻寂,無法下載該插件)息尺;
(4)若下載成功則重新打開該拆件,否則疾掰,關(guān)閉頁面搂誉,并提示失敗。
4静檬、打開離線插件的流程:
一個(gè)打開plugin的demo協(xié)議是這樣的:
demo://plugin?pluginId=xxx&pageName=xxx
(1)根據(jù)pluginId炭懊,獲取該插件的目錄pluginDir,一般為/data/data/package name/pluginId
(2)進(jìn)入插件目錄拂檩,找到config.json文件侮腹,并將其讀取為WebPluginConfigModel;
(3)根據(jù)的pageName广恢,可以查找到對應(yīng)頁面的pagePath凯旋;
(4)通過BridgeWebView打開本地頁面"file://" + pluginDir + pageFilePath + "?" + query。
5、離線插件數(shù)據(jù)預(yù)加載優(yōu)化
為了進(jìn)一步加快離線包的頁面顯示速度至非,提出了離線包預(yù)加載數(shù)據(jù)钠署,webview打開一個(gè)url之前,發(fā)起一個(gè)本地網(wǎng)絡(luò)請求去請求即將打開的h5頁面的數(shù)據(jù)荒椭。
1谐鼎、預(yù)加載H5頁面的前提:
在離線包的config.json文件中為需要預(yù)加載的頁面添加預(yù)加載數(shù)據(jù)PreloadRequest,主要包含預(yù)加載的url趣惠,請求方法狸棍,請求參數(shù)等。
2味悄、H5離線插件頁面預(yù)加載數(shù)據(jù)流程如下:
打開預(yù)加載h5頁面的url一般是
demo://plugin?pluginId=xxx&pageName=xxx&requestPreload=true
(1)首先根據(jù)pluginId從對應(yīng)插件的config配置文件中讀取相應(yīng)頁面的預(yù)加載數(shù)據(jù)PreloadRequest草戈。
(2)根據(jù)預(yù)加載數(shù)據(jù)PreloadRequest中的url,請求方法侍瑟,請求參數(shù)等利用本地okhttp框架構(gòu)建一個(gè)網(wǎng)絡(luò)請求唐片,去獲取數(shù)據(jù)。
(3)當(dāng)數(shù)據(jù)獲取成功涨颜,如果jsbridge注入完成费韭,則調(diào)用其回調(diào)方法PreloadFinish,將獲取到的數(shù)據(jù)傳遞給h5頁面庭瑰;如果jsbridge未注入完成星持,則將獲取的數(shù)據(jù)緩存到內(nèi)存,待jsbridge注入完成弹灭,在其注入完成的回調(diào)中督暂,調(diào)用h5頁面的回調(diào)方法PreloadFinish將數(shù)據(jù)傳遞給h5頁面。h5頁面收到數(shù)據(jù)后鲤屡,并可根據(jù)數(shù)據(jù)去填充頁面损痰。
注:我們用一個(gè)boolean的變量標(biāo)記jsbridge是否注入完成,jsbridge注入完成酒来,會有一個(gè)回調(diào),我們可以在回調(diào)中將這個(gè)變量置位true肪凛,標(biāo)記注入完成堰汉。