Hybrid 虐我千百遍?that's impossible
前言
其實對於寫文章這個事,我的內心是拒絕的,概括一下心路歷程就是雖然嘴上說著不要不要,但是身體卻很誠實,其實內心拒絕的原因是不知如何讓枯燥的技術文兒看起來能更具吸引力,於是決定想一個極其正(you)經(huo)的標題,作為一個既不是八卦也不是爆料的技術軟文來講,給“娃”起名這個事就是邁出的艱難一步,所以標題就成了上面那樣嬸兒的了…
背景
隨著同鎮側產品的穩定前行,工作的關注點也從純業務研發轉向效能優化和使用者體驗,而後在使用bugly對app崩潰情況進行檢視時,發現線上崩潰總數的70%來自系統的UIWebView,而崩潰的主要元凶——爆記憶體
看來UIWebView飽受詬病的載入網頁會造成記憶體無限增長的問題還是沒有得到很好的解決,隨著蘋果在iOS 8之後推出了WKWebView,其相對UIWebView有著更快的載入速度, 更低的記憶體佔用,這些突出的優點讓我們看到了解決眼下問題的方法,於是這套新Hybrid框架應運而生。
正文
這裡先來領略一下新Hybrid框架圖,大概長這樣...
新框架由三大基礎部分組成,分別是Module、Bridge和HybridVC,下面先簡單介紹一下各自的職責,後續會對每個基礎部分進行詳細的說明。
-
Module(元件):包含所有業務Module,每一個Module都必須遵從“ModuleProtocol”協議,協議主要規定了Module具備的能力、屬性和註冊方法
-
Bridge(橋接器):Module元件與檢視控制器HybridVC之間為解耦狀態,相互之間保持隔離,Bridge負責對上述兩者進行橋接並維護它們之間的業務邏輯邏輯關係
-
HybridVC(檢視控制器):業務H5頁面的基類,承載著兩套WebView檢視
一、Module元件
Module是Hybrid框架中業務邏輯實現的節點,針對業務H5頁面中不同iFrame請求攜帶的不同action行為來區分由哪個Module節點來進行接收並處理。每一個Module元件必須遵從“ModuleProtocol”協議,協議中內容如下圖:
moduleActionName為指定當前Module對應action名稱、responseAction為當前Module所對應的業務實現方法、協議提供的兩個optional可選的屬性,為別是橋接器bridge和params(action攜帶的引數),Module可以依據自身需要選擇是否通過@synthesize方法來得到這些可選的成員變數實現業務邏輯。
最後可以看到定義的三個巨集,分別是①WBT_EXPORT_MODULE、②WBT_EXTERN_HYBRID_MODULE和③WBT_HYBRID_MODULE_END
-
WBT_EXPORT_MODULE:該巨集定義做了二件事,第一、將自身註冊至bridge橋接器的moduleMap,第二、實現“ModuleProtocol”協議中的moduleActionName方法。巨集使用方法如下圖所示
-
WBT_EXTERN_HYBRID_MODULE:相比第一個巨集,WBT_EXTERN_HYBRID_MODULE完成了一個更高階的分裝,其直接宣告並實現了一個Module的類,類中直接完成了類的宣告、成員變數獲取和對應協議方法的實現,使用方法如下圖所示
-
WBT_HYBRID_MODULE_END:@end結束符
二、Bridge橋接器
為了解決業務耦合問題,Module元件與檢視控制器HybridVC之間不可以進行互動,全部由Bridge進行橋接,橋接器主要承載著檢視業務邏輯處理和業務分發的功能,橋接器類的.h標頭檔案包含一個初始化方法和一個delegate屬性,.m實現檔案包含一個全域性唯一WBTModuleClassMap對映字典、一個WBTRegisterModule註冊方法和類私有屬性moduleMap,接下來針對每個組成部分進行進一步說明。
-
初始化方法:自定義了Bridge的初始化行為
-
delegate屬性:需要遵從“BridgeProtocol”協議,協議中定義了一些optional的可選方法,如下圖所示
-
bridge初始化方法:可以在初始化bridge物件的同時指定delegate屬性所指向的物件(通常為載體頁HybridVC),UIWebView與WKWebView的檢視回撥邏輯也同樣由bridge統一處理,通過協議方法橋接器可以將web頁面載入的生命週期行為進行回撥
-
RegisterModule方法:方法中定義了一個全域性唯一的字典WBTModuleCla ssMap,該字典的每個KV鍵值對代表action的名稱和aciton所對應的Module類名稱,這樣在應用使用期間可能出現多個Hybrid載體頁的情況下,每個載體頁的moduleMap私有屬性只需要從WBTModuleClassMap獲取一份拷貝即可,這保證了多載體頁情景下資料來源不會互相汙染,WBT_EXPORT_MODULE巨集中也是通過在+load中呼叫該方法完成了將自身註冊到WBTModuleClassMap中的行為。
在使用私有屬性moduleMap的同時,為了節約記憶體採用了懶載入的方式,即當我們第一次呼叫actionA時,如果發現在moduleMap中actionA對應的ModuleClass是一個類而非物件時,代表當前acitonA還沒有使用過,這時會將該ModuleClass初始化為一個物件,並重新插入moduleMap,覆蓋當前的value,這樣在下次使用的過程中就不需要再進行初始化。這種方式式避免了在一開始就將所有的ModuleClass都初始化為物件造成的資源浪費情況。
當iFrame請求被webView截獲後,就會交由bridge進行分發處理,bridge此時就會依據moduleMap來找到對應的元件並執行responseAction方法。
三、檢視控制器
檢視控制器負責承載UIWebView和WKWebView兩種型別的檢視,hybridVC具備三個屬性,分別為bridge、hybridType和progressView。
-
檢視控制器物件會強持有bridge屬性,以保障在業務互動過程中bridge不會被自動回收
-
hybridType為一個頁面型別欄位,用於在檢視控制器初始化的過程中決定當前檢視控制器承載的檢視view是UIWebView還是WKWebView,在UIWebView顯得如此不堪的情況仍然提供支援的原因在於為了保障框架整體的正常執行,在極端情況 下可以依據協議來對控制載入的檢視型別進行動態控制,以保障線上穩定執行
-
progressView為一個載入進度條控制元件,由於UIWebView與WKWebView在載入檢視和使用過程中無法直接辨認,所以當載入的檢視為WKWebView時會在檢視頂部新增一個載入進度條,用以對當前的檢視型別進行區分
在webView檢視的配置過程中,還用到了一個小技巧,大多數開發者在新增webView檢視的時候都習慣使用addSubview的方式將當前檢視新增到檢視控制器的view上,這樣一來,webView檢視的適配工作就需要由我們自己來進行管理,隨著機型尺寸的變動就需要每次對webView的frame進行計算。在這裡,我們直接使用賦值的方式即self.view=webView,這樣view檢視(也就是webView)的生命週期、frame等資訊將會由檢視控制器自行管理,不再需要我們進行人為干預。
四、總結
到這裡Hybrid框架的部分就講完了,目前該框架已經應用於同鎮B端APP,框架上線之後崩潰率也正如我們期待的那樣,整體下降70%,接下來同鎮C端APP也會完成本套框架的接入,如果對文章有疑問或者指正的同學可以及時聯絡我
使用者體驗的每一次提升都是我們作為技術人的一次成長里程碑,我們堅信未來會為使用者創造更加完美的使用者體驗,併為此不斷努力著...