Shield——開源的移動端頁面模塊化開發框架
引言
一直以來,如何能更高效地開發與維護頁面是Android與iOS開發同學最主要的工作和最關心的問題。隨著業務的不斷發展,根據特定業務場景產生的定製化需求變得越來越多。單一頁面往往需要根據不同業務、不同場景甚至不同用戶展示不同的內容。在這樣的背景下,我們開始考慮對頁面進行切分,把一個頁面切分成多個模塊,以提高複雜頁面的可維護性。
各種不同的定製化頁面如下:
Shield是美團點評到店綜合團隊模塊化UI界面解決方案,它不僅僅是一個Native(Android&iOS)的UI開發框架,還是到店綜合團隊基於自身複雜的業務場景沉澱出來的UI開發最佳實踐。它具備高可復用、容易協同開發等特性,還包括後端動態配置、動態模塊等一系列解決方案,目前已經在GitHub上開源:https://github.com/Meituan-Dianping/Shield。
什麼是模塊
在Shield框架里,頁面是由一個個模塊(Agent)組成的。模塊是頁面中粗粒度的抽象組件,包含部分頁面UI展示和與之相關的業務邏輯。這些模塊按線性的方式排布在頁面中,可以很靈活地調換位置且互不影響。每個模塊都有自己獨立的生命周期,可以單獨通過網路獲取數據、渲染視圖等等。
每一個模塊都有自己獨立的邏輯和UI,模塊之間完全解耦,這樣就可以很方便地通過排列模塊來完成不同的頁面定製化需求,使一個頁面可以展示不同的內容。同時,由於模塊並不依賴某一具體頁面,模塊也可以在不同的頁面之間進行復用。
不同於MVP或是MVVM的設計模式,Shield的模塊化拆分方式根據視圖和業務邏輯對頁面進行橫向切分。模塊化的拆分與MVP等架構方式的拆分並不衝突。開發者完全可以在Shield的某個模塊里運用MVP或MVVM的架構方式,來對頁面的邏輯進行進一步的拆分以提升代碼復用性,使模塊邏輯變得更加清晰。
為了更好地抽象UI界面開發的各種場景,Shield框架賦予了模塊完整的頁面能力,包括完整的頁面生命周期和上下文環境(Context)等。這樣模塊的開發方式與原有的頁面開發方式完全一致,頁面不再關心具體的UI展現,而是把這些都交給模塊。同時模塊可以單獨開發維護,運行在任意接入了Shield框架的頁面中。
以下是模塊Agent的介面定義:
public interface AgentInterface {nn void onCreate(Bundle savedInstanceState);nn void onStart();nn void onResume();nn void onPause();nn void onStop();nn void onDestroy();nn Bundle saveInstanceState();nn void onActivityResult(int requestCode, int resultCode, Intent data);nn String getIndex();nn void setIndex(String index);nn String getHostName();nn void setHostName(String hostName);nn SectionCellInterface getSectionCellInterface();nn String getAgentCellName();n}n
一個Agent模塊的結構主要包含兩部分:
- 生命周期回調。
- 提供一個SectionCellInterface。
其中,SectionCellInterface是模塊的視圖邏輯抽象。一個模塊可以為頁面提供一個連續的包含多塊(Section)的UI片段,每一塊視圖可以是視覺上的單行(Row)視圖,也可以是多行視圖。具體的介面定義如下:
public interface SectionCellInterface {nn int getSectionCount();nn int getRowCount(int sectionPosition);nn int getViewType(int sectionPosition, int rowPosition);nn int getViewTypeCount();nn View onCreateView(ViewGroup parent, int viewType);nn void updateView(View view, int sectionPosition, int rowPosition, ViewGroup parent);nn}n
一個模塊化頁面的組成結構
有了模塊承擔絕大部分的頁面邏輯,Shield框架中的頁面就變成了一個單純的模塊容器。頁面通過不同的模塊配置(Config)來靈活改變自己的視圖展現,同時在模塊配置(Config)中,定義了模塊的位置信息,這樣除了本地配置之外,Shield框架也可以很容易就能支持後端動態下發模塊配置,以達到客戶端的一定動態性。
在接入了Shield框架的頁面中,還有兩個比較重要的角色,分別是模塊管理器(AgentManager)和視圖管理器(CellManager)。
其中,模塊管理器(AgentManager)負責模塊的創建、銷毀、生命周期分發等工作。而視圖管理器(CellManager)則負責將模塊所提供的視圖片段(SectionCellInterface)有序地添加到頁面中,並在適當的時候對這些視圖進行更新。
模塊通信
在某些場景下,頁面中的一些視圖片段會根據用戶操作發生一些聯動。而當這些視圖片段處於不同的模塊中時,這些模塊就需要進行通信。
在這種情況下,如果讓模塊與模塊直接進行交互,就無法避免模塊之間的耦合,這樣既無法保證模塊的獨立性,也影響可復用性。於是我們基於RxJava設計實現了觀察者模式的白板組件,在Shield框架中稱之為WhiteBoard。WhiteBoard在一個頁面中唯一,所有模塊共享,模塊之間或是模塊與頁面的通信都通過WhiteBoard來進行。
Shield框架關注的重點
靈活配置
只要把模塊配置放到遠端,通過統一的配置後台進行配置,就可以很輕鬆地實現App中各個頁面一定的動態化特性,無需藉助其它插件化、熱補丁等方案。
下圖便是美團點評開發的頁面模塊配置後台:
多端統一
我們通過提供多端統一的模塊化框架,減少開發者在不同平台的視覺實現差異上耗費的精力,從而將精力集中於如何實現具體的視圖片段。Shield框架針對Native開發中常見的畫分隔線、loading動畫等一系列場景做了抽象,為模塊提供了豐富的定製化功能,簡化了App開發過程中佔比較高的視圖開發工作。
動態化
模塊化框架對模塊的業務和視圖邏輯行為都做了一定的抽象,這樣,ReactNative一類的動態化方案不僅可以運用到視圖繪製層面上,同時也可以通過不同的JSBridge實現模塊業務邏輯的動態化。而配置後台不僅可以動態調整模塊,同時可以動態調整模塊的內部展示,這樣整個模塊化框架可以通過配置後台實現不同粒度的頁面動態化方案。有關動態模塊的相關方案,後續將另文詳述。
頁面混排與穩定性
藉助於模塊化框架,可以有效地降低諸如ReactNative等開源框架的接入成本,無需對整個頁面進行改造,而是在模塊級的粒度上進行快速試錯,有效控制影響範圍,提升頁面整體的穩定性。
圍繞模塊化框架的工具鏈及生態圈
我們還在逐步建設圍繞模塊化框架的工具鏈及生態圈,包括基於模塊的自動化測試、聲明式打點、動態化等項目。
結語
在美團點評的多業務線運營背景下,大部分頁面通過Activity+Fragment+Agent的模塊化架構支撐了大量的業務差異化定製需求。同時我們結合業務特點,沉澱了列表型模塊、Tab錨點型模塊等多種組件型模塊。除了提升開發效率外,模塊化框架在我們針對各業務解耦、跨Team協同開發等方面也扮演了重要的角色。
希望大家多多支持我們的開源項目Shield,也歡迎大家多提意見,互相交流移動端架構方面的經驗與心得。
招聘
美團點評到店綜合前端研發中心招聘,大量職位open。
- 前端研發工程師。
- Node開發工程師。
- iOS/Android客戶端開發工程師。
歡迎實習生(2019年及以後畢業)、2018年畢業生及社招同學踴躍投遞簡歷。
工作描述:
- 負責各業務前端研發工作,涵蓋Mobile Web、小程序、App、自研硬體等多終端平台。
- 負責前端開發、測試、上線、監控系統研發。
- 負責開放平台、第三方開發者平台系統構建及研發。
有意者請郵件至:nihao#http://meituan.com。
推薦閱讀:
※談談開源(一)
※最新數據報告揭示開源技術的投資回報
※github 中項目演示中的動態圖片是怎麼製作的?
※如何給開源項目起一個簡潔,上口的項目名稱?