???????Android?H5通用容器架構(gòu)設(shè)計詳解
背景
大家如果經(jīng)歷過Hybrid項目的開發(fā),即項目中涉及到H5與Native之間的交互,那么很有可能會遇到各種各樣的H5容器。為什么會有那么多各種各樣的容器呢...這也是輪子多的通病了,輪子多到業(yè)務(wù)方不知道選哪個。當然,也有可能大家壓根就不會使用到H5容器,直接用系統(tǒng)WebView就完事兒了,比如我的前東家就是這樣做的。那這篇文章的主題就是與前者相關(guān)的:當項目中擁有很多個H5容器時,怎樣封裝才能讓業(yè)務(wù)側(cè)用得爽呢?
下面按慣例,拋出這篇文章要解決的三個問題:
- 如何優(yōu)雅地提供接口調(diào)用?
- 怎樣封裝多個不同類型的H5容器?
- 這樣的架構(gòu)能帶來什么樣的好處?
術(shù)語對齊
術(shù)語 | 描述 |
---|---|
H5框架容器 | 特指目前項目中正在使用的二方/三方H5容器。 |
H5通用容器 | 特指將項目中所有的框架容器抽象到一個容器中,讓業(yè)務(wù)側(cè)不需要感知到具體的框架細節(jié)。 |
A頁面 | url query參數(shù)中帶a=true的頁面,項目中用A容器承載。 |
B頁面 | url query參數(shù)中帶b=true的頁面,項目中用B容器承載。 |
探索
如何優(yōu)雅地提供接口調(diào)用?
業(yè)務(wù)側(cè)調(diào)用的接口其實很有限,可以說90%的業(yè)務(wù)都只是打開一個網(wǎng)頁而已。
我們根據(jù)業(yè)務(wù)需要新建一個接口類:
/** * WebPage服務(wù)接口類 */ public interface IWebPageService { /** * 打開url * @param url */ void openUrl(String url); /** * 創(chuàng)建Fragment內(nèi)置到某頁面中使用 * @return */ Fragment buildFragment(String url, Context context); /** * 獲取當前Url * @return */ String getCurrentUrl(); /** * 給h5發(fā)通知 * @param eventData * @param eventName */ void postNotificationToJS(String eventName, String eventData); }
以上列舉了4個非常常見的接口。如果有需要,還可以擴展其它的,比如設(shè)置容器生命周期相關(guān)的監(jiān)聽、設(shè)置H5發(fā)通知來時的監(jiān)聽等等。拿openUrl
的實現(xiàn)舉例,實現(xiàn)類通過url中的參數(shù)判斷需要打開A容器
還是B容器
。
@Override public void openUrl(String url) { // 若url中含有A參數(shù),則用A容器打開 // if (urlParamHasA) { // startAContainer(); // } else { // 否則用B容器打開 // startBContainer(); //} }
這就從接口層這一層面屏蔽了內(nèi)部框架容器。其它接口也是類似,原則就是不讓業(yè)務(wù)側(cè)感知到具體的實現(xiàn)細節(jié),而不是跳個頁面還需要知道這個url是要用A容器打開還是B容器打開。在應(yīng)用啟動時,可以將實現(xiàn)類注入到公共依賴中去,也可以通過ARouter等框架實現(xiàn)依賴注入,方便業(yè)務(wù)側(cè)的調(diào)用。
怎樣封裝多個不同類型的H5容器容器?
整體架構(gòu)
先上一張圖:
接口層上面已經(jīng)講過了,這里我們直接看容器層。
通用容器
第一層就是我們抽出來的通用容器,WebPageActivity與WebPageFragement。項目中,不管是哪個H5框架容器,勢必都是用Activity或者Fragment來承載的,所以我們相應(yīng)的也需要有這兩者作為父類去進行封裝。而通用容器中,最重要的職責就是去執(zhí)行通用邏輯,即每個框架容器都需要執(zhí)行的邏輯。
通用職責可以有以下幾點:
- 設(shè)置頁面屬性,包括window flag、activity的主題等。
- 解析url,將url中的關(guān)鍵參數(shù)記錄下來,從而決定以哪種策略渲染UI。比如可以通過disableNav參數(shù)決定是否顯示導(dǎo)航欄,通過statusBarStyle參數(shù)決定狀態(tài)欄的風格等。
- 控制通用UI的渲染,包括何時改變TitleBar、何時加載/隱藏Loading,何時渲染異常態(tài)UI等。
- 決定框架容器加載時的邏輯順序,如setContentView()->loadUrl()。
- 接收生命周期事件與系統(tǒng)回調(diào)事件,并且分發(fā)回調(diào)。生命周期就不用說了,系統(tǒng)回調(diào)這里特指
onActivityResult
、onRequestPermissionResult
這類事件,然后將結(jié)果分發(fā)給各listener。
拿WebPageActivity#onCreate
舉例:
@Override protected void onCreate(Bundle savedInstanceState) { // setWindowFlags(); 設(shè)置window屬性 // setTheme(); 設(shè)置主題 // setTransition(); 設(shè)置轉(zhuǎn)場動畫 super.onCreate(savedInstanceState); // parseUrl(); 解析url // initCommonUI(); 渲染通用UI 比如TitleBar、底部導(dǎo)航欄等 setContentView(); loadUrl(); } protected abstract void setContentView(); protected abstract void loadUrl();
我們可以將設(shè)置window屬性、設(shè)置主題這些通用行為放在通用容器中執(zhí)行,而setContentView
與loadUrl
交由框架容器去執(zhí)行。setContentView
是因為不同的容器,layout xml可能是不一樣的。loadUrl
是因為每個容器用WebView去加載url的方式也可能不同,無法統(tǒng)一。類似的行為,若執(zhí)行時機可統(tǒng)一,則由通用容器統(tǒng)一執(zhí)行時機,比如setContentView
與loadUrl
。若時機也沒法確定,甚至邏輯只會存在于某個框架容器中,那么就要寫到子類中去了。
框架容器
圖中第二層的容器A、容器B、容器C指的就是框架容器。有了通用容器承載共同的邏輯后,框架容器需要做的事情就比較少了。只需要加載各自的布局,初始化WebView,然后將url加載到WebView中就可以了(除非有某個框架容器需要特殊適配,那就得寫些額外的代碼)。具體的實現(xiàn)需要根據(jù)項目中框架容器的使用方式來確定。
基礎(chǔ)組件
第三層表示基礎(chǔ)組件。這里只列一些常見的基礎(chǔ)組件。
- Components:各類UI組件,比如TitleBar與異常態(tài)控件。
- JS Bridge:JS Bridge可以單獨放到一個類中做收口,然后分發(fā)給各個listener。listenr通過接口層進行注入。
- WebViewClient:WebViewClient在日常開發(fā)中算是維護的非常頻繁的一個類,職責主要是在
shouldOverrideUrlLoading
重定向時對url進行攔截,以及根據(jù)相應(yīng)的生命周期回調(diào)(onPageStarted、onPageFinished、onReceviedError
等)進行l(wèi)oading或異常態(tài)UI的渲染時。若每個框架容器可以設(shè)置同一個WebViewClient,那就非常方便了,只要改一處地方,所有容器就都生效了。 - Interceptors:即用于WebView重定向時的攔截器。像我的項目中會有很多攔截相關(guān)的邏輯,比如將A頁面攔截跳轉(zhuǎn)到原生頁面,就需要在重定向時進行攔截。但邏輯堆疊太多會提高維護的成本,因此建議用責任鏈模式(沒用過的同學(xué)可以百度一下,很簡單滴~)去處理攔截的這塊邏輯。
- Managers、Listeners:容器中總是需要很多輔助類去來幫助我們?nèi)ス芾硪恍〇|西,比如WebView的管理,url的管理。這里就不延伸了,根據(jù)實際需要去加。Listener也一樣,關(guān)于容器生命周期的Listener,與JS Bridge交互相關(guān)的Listener,都可以通過接口層去實現(xiàn)注入與銷毀。
當然,除此之外,根據(jù)每個項目的完整度與負責度,還會衍生出很多基礎(chǔ)組件。比如H5的監(jiān)控體系,H5的預(yù)加載體系,以及WebView預(yù)創(chuàng)建相關(guān)的緩存體系等。他們作為框架容器共同的底座承擔相應(yīng)的職責。
這樣的架構(gòu)能帶來什么樣的好處?
- 對業(yè)務(wù)側(cè)而言,接口統(tǒng)一收口到IWebPageService中去,調(diào)用方式更加簡單明了,不需要再關(guān)注容器的具體類型。
- 對容器側(cè)而言,代碼邏輯性清晰的同時,有效降低了日后的維護成本與開發(fā)成本。Loading、異常態(tài)不需要再在各類容器中重復(fù)寫N遍,各種url params都統(tǒng)一收口到通用容器中去,重定向攔截邏輯也不再需要重復(fù)寫N遍...總而言之,通用邏輯統(tǒng)一給通用容器與基礎(chǔ)組件去承擔,框架容器的代碼,都是各框架容器所“獨有”的。
到此這篇關(guān)于Android H5通用容器架構(gòu)設(shè)計詳解的文章就介紹到這了,更多相關(guān)Android H5容器架構(gòu)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
android使用ViewPager組件實現(xiàn)app引導(dǎo)查看頁面
這篇文章主要為大家詳細介紹了android使用ViewPager組件實現(xiàn)app引導(dǎo)查看頁面,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07Android?APP瘦身shrinkResources使用問題詳解
這篇文章主要為大家介紹了Android?APP瘦身shrinkResources使用問題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11Android 限制edittext 整數(shù)和小數(shù)位數(shù) 過濾器(詳解)
下面小編就為大家?guī)硪黄狝ndroid 限制edittext 整數(shù)和小數(shù)位數(shù) 過濾器(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04Android開發(fā) -- 狀態(tài)欄通知Notification、NotificationManager詳解
本文主要講解狀態(tài)欄通知Notification、NotificationManager,小編覺得非常詳細,給大家一個參考,希望對大家學(xué)習有所幫助。2016-06-06Android實現(xiàn)視頻播放--騰訊瀏覽服務(wù)(TBS)功能
TBS視頻播放器可以支持市面上幾乎所有的視頻格式,包括mp4, flv, avi, 3gp, webm, ts, ogv, m3u8, asf, wmv, rm, rmvb, mov, mkv等18種視頻格式。這篇文章主要介紹了Android實現(xiàn)視頻播放--騰訊瀏覽服務(wù)(TBS),需要的朋友可以參考下2018-07-07Android?內(nèi)存優(yōu)化知識點梳理總結(jié)
這篇文章主要介紹了Android?內(nèi)存優(yōu)化知識點梳理總結(jié),Android?操作系統(tǒng)給每個進程都會分配指定額度的內(nèi)存空間,App?使用內(nèi)存來進行快速的文件訪問交互,長時間如此便需要優(yōu)化策略,文章分享優(yōu)化知識點總結(jié),需要的朋友可以參考一下2022-06-06