Android中Hybrid實戰
目錄
1、專案中Hybrid整體結構
2、橋接層
3、基礎通訊層
4、開啟離線外掛的流程
5、離線外掛資料預載入優化
1、我的專案中Hybrid層次結構

我的專案中的Hybrid結構層次
(1)H5頁面層。
(2)橋接層:BridgeJs是一個.js檔案,是NA和H5通訊的橋樑,WebView在載入url之前需要將BridgeJs前置注入。
(3)基礎通訊層:該層主要由BridgeWebView、BridgeManager、WebPlugin組成,BridgeWebView提供了基本的頁面載入,並捕獲BridgeJs傳送過來的事件交給BridgeManager進行處理;BridgeManager具備BridgeWebView的控制能力,負責處理NA向H5以及H5向NA的訊息處理;WebPlugin是離線化外掛,為了加速H5頁面的展示,可將某個業務的h5、css、js、圖片等資源打包,並離線化至本地,在開啟相應頁面的時候只需獲取其對應頁面的資料,省去h5、css、js、圖片等資源的下載時間。
(4)協議分發層:該層是一個總體的bdwm協議分發器,將收到的協議分發到各個協議實現層進行處理。
(5)協議實現層:該層針對bdwm協議不同的scheme,分別對應不同的實現。NativePageCall用於APP內各個頁面的跳轉;WebSDKCall是用於為H5提供各種NA能力,WebPluginCall用於開啟本地離線化外掛。
(6)Native層:該層為客戶端App層,為上述幾種協議提供能力呼叫和支援。
2、橋接層
2.1、BridgeJs是什麼?
BridgeJs是一個.js檔案,是NA和H5通訊的橋樑,具體來說就是H5呼叫NA能力或者開啟NA頁面必須通過呼叫BridgeJs中的方法來通知NA,NA也必須呼叫BridgeJs中的方法來給H5頁面傳送訊息。
2.2、BridgeJs的注入方式
WebView注入BridgeJs檔案的方式為,先將該檔案讀入記憶體作為BridgeJs,Android4.4以前通過loadUrl("javascript:" + BridgeJs)進行注入,BridgeJS注入完畢後,在JS函式尾部通過onConsoleMessage向NA發起通知,標記注入完畢的事件;4.4後的版本,可使用evaluateJavascript (String script, ValueCallback<String> resultCallback)方法注入並實現回撥,注入完成後可向對應H5頁面種入NA基本資訊,供H5使用並呼叫H5中Ready方法觸發H5頁面渲染。
3、 基礎通訊層
3.1、BridgeWebView
由WebView+TitleBar+ProgressBar構成,ProgressBar根據onProgressChanged(WebView view, int newProgress)顯示當前頁面載入進度,避免頁面載入時無狀態,TitleBar支援多種主題,為H5提供三種UI操作元素(返回按鈕,標題,功能按鈕);WebView是作為H5頁面的容器,在載入頁面的同時,也負責捕獲頁面訊息和注入JS。
3.2、BridgeManager
通過重寫shouldOverrideUrlLoading(WebView view, String url),將收到的重定向url交給BridgeManager去攔截,BridgeManager通過呼叫SchemeDispatcher. onDispatch(String url)的去處理訊息;如果BridgeWebView主動向H5傳送訊息,則通過BridgeManager執行相應的javascript方法。
3.3、 WebPlugin
3.3.1、離線外掛整體思路

離線外掛整體思路.png
(1)、運營平臺上傳打包好的離線外掛;
(2)、server端發下離線外掛配置;
(3)、App端根據server下發配置下載和更新離線外掛。
(2.1)、離線外掛包括html、css、js、img和config.json檔案。
config.json檔案是外掛配置檔案,記錄外掛包含的頁面及其頁面路徑,是一段json資料,例如:

config檔案
(3.1)、server端下發離線外掛配置包括一個allMd5值和一個plugin_list外掛列表,外層的md5表示所有外掛zip包共同計算的總md5值,用於和本地總md5對比,判斷是否有外掛要更新。plugin_list中每個外掛包含plugin_id,url和md5,plugin_id唯一標識該外掛,並作為data/data/package name/WMPlugins/目錄下外掛的路徑名,url為外掛下載地址,md5為外掛zip包的md5值,用於判斷當前外掛是否需要更新及下載完成後校驗該包的完整性。
離線外掛分為正常更新和緊急更新:
3.3.2離線外掛的正常更新

離線外掛正常更新.png
(1)首先檢測當前是否有更新任務佇列正在執行,如果有,則直接返回;
(2)檢測介面返回的allMD5值與本地sharedPreferences記錄的上次成功更新的總md5值是否相同,如果相同,則直接返回,否則繼續;
(3)為每個plugin配置更新任務PluginUpdateTask,然後放到執行緒池中執行,且所有的Task及其狀態被記錄在一個HashMap<String, PluginUpdateState>中,用於判斷是否所有的外掛都更新完成並且成功;
(4)PluginUpdateTask實現單個外掛的更新流程:先檢查本地相同pluginId的md5是否與新的相同,若相同,則直接返回True,否則,依次執行外掛下載、md5完整性校驗、刪除舊外掛、解壓,最後將該外掛新的md5值記錄到本地。(每個外掛下載成功或者失敗都將其狀態記錄到HashMap<String, PluginUpdateState>中);
(5)檢測是否所有的外掛更新完成並且成功,如果是,則將allMd5值記錄到本地,否則等待下次更新或者緊急更新。
3.3.3、離線包緊急更新
Plugin緊急更新用於一些極端case,在某些場景下,使用者點選進入離線外掛,可能會遭遇開啟失敗(例如外掛被清理,上一次更新異常等),這種情況下,需要根據外掛的pluginId,為該plugin開啟獨立的外掛下載任務,且不與正常的更新檢測任務耦合。流程如下:

離線外掛緊急更新
(1)外掛啟動失敗,進入BridgeWebView頁,顯示loading;
(2)根據pluginId,查詢記憶體中的PluginBean,若找到,則直接開始下載,若未找到,則重新拉取離線外掛介面,獲取PluginBean,
(3)為PluginBean配置PluginEmergencyTask,放到執行緒池中開啟緊急下載任務,複用單個外掛下載邏輯下載該外掛。(不同的是此時不需要對該plugin新的md5值和本地md5只校驗,因為如果是該外掛正常情況下下載成功,但是被刪除,校驗md5值會直接返回,無法下載該外掛);
(4)若下載成功則重新開啟該拆件,否則,關閉頁面,並提示失敗。
4、開啟離線外掛的流程:

開啟離線外掛的流程
一個開啟plugin的bdwm協議是這樣的:
demo://plugin?pluginId=xxx&pageName=xxx(1)根據pluginId,獲取該外掛的目錄pluginDir,一般為/data/data/package name/pluginId
(2)進入外掛目錄,找到config.json檔案,並將其讀取為WebPluginConfigModel;
(3)根據的pageName,可以查詢到對應頁面的pagePath;
(4)通過BridgeWebView開啟本地頁面"file://" + pluginDir + pageFilePath + "?" + query。
5、離線外掛資料預載入優化
為了進一步加快離線包的頁面顯示速度,提出了離線包預載入資料,webview開啟一個url之前,發起一個本地網路請求去請求即將開啟的h5頁面的資料。
1、預載入H5頁面的前提:
在離線包的config.json檔案中為需要預載入的頁面新增預載入資料PreloadRequest,主要包含預載入的url,請求方法,請求引數等。
2、H5離線外掛頁面預載入資料流程如下:

H5離線外掛資料預載入
開啟預載入h5頁面的url一般是
demo://plugin?pluginId=xxx&pageName=xxx&requestPreload=true(1)首先根據pluginId從對應外掛的config配置檔案中讀取相應頁面的預載入資料PreloadRequest。
(2)根據預載入資料PreloadRequest中的url,請求方法,請求引數等利用本地okhttp框架構建一個網路請求,去獲取資料。
(3)當資料獲取成功,如果jsbridge注入完成,則呼叫其回撥方法PreloadFinish,將獲取到的資料傳遞給h5頁面;如果jsbridge未注入完成,則將獲取的資料快取到記憶體,待jsbridge注入完成,在其注入完成的回撥中,呼叫h5頁面的回撥方法PreloadFinish將資料傳遞給h5頁面。h5頁面收到資料後,並可根據資料去填充頁面。
注:我們用一個boolean的變數標記jsbridge是否注入完成,jsbridge注入完成,會有一個回撥,我們可以在回撥中將這個變數置位true,標記注入完成。