MVP“變形記”,蘇寧移動開發的架構演進!
原創【51CTO.com原創稿件】蘇寧+App 是蘇寧易購集團零售云研發中心主要產品之一,由于項目處于初期階段,業務邏輯復雜,導致業務需求變動快,常常在開發甚至測試過程中出現界面或者后臺接口調整的情況。
App 客戶端如何在外部需求不斷變化的情況下,降低模塊耦合,盡可能減少每次代碼修改量,一方面減少開發人員的工作量,另一方面降低測試工程師的工作量,最終順利完成項目迭代開發。
為什么使用 MVP 模式
相信在 2014 年之前,絕大部分人開發 Android 應用,都是使用的 MVC 模式。
M 跟 V 一般沒有什么問題,Controller 層也就是對應 Activity 類,它的首要職責是加載應用的布局和初始化用戶界面,并接受和處理來自用戶的操作請求,進而作出響應。
隨著界面及其邏輯的復雜度不斷提升,Activity 類的職責不斷增加,以致變得龐大臃腫,打開以前項目的 Activity,超過 2000 行的不在少數。
另外,由于項目的特殊性,互聯網產品講究速度,尤其是新產品,上線的時間會決定你在市場上的占有量。
App 的理想情況是 UED 做好視覺稿,后臺接口準備完畢,客戶端同學一邊做頁面一邊調試接口,做完自測后順利交付測試。但是,理想很豐滿,現實很骨感。
實際情況是,我們開始 Coding 的時候,只有一份接口文檔跟交互圖。我們需要思考的是,我們必須把界面和接口數據解耦,接口聯調和測試工作不能依賴界面的完成,當完成業務層代碼后,就可以測試業務功能。
基于上面的背景,我們選擇了 MVP 模式。
什么是 MVP 模式
我們上面說的 MVP 架構,是 Google 開源的一個設計模式,它主要是為了細分視圖(View)與模型(Model)的功能,讓 View 只做兩件事:
- 完成用戶的交互。
- 顯示界面布局,同時讓 Model 做數據的處理,業務邏輯放到另外的一個類(Presenter)中。
下面做具體分析:
- M 即 M 層,在項目中負責數據的處理,包括本地數據庫查詢,網絡數據獲取都在這一層中完成。
- View 即 V 層,在項目中是 UI 模塊,也就是各種 activity/fragment,負責繪制 UI 元素、與用戶進行交互。
- P 即 P 層,在項目中做為 View 與 Model 的橋梁,M 跟 V 層不直接交互,M 層在獲取到數據之后,傳遞到 P,P 層再通過接口回調到 View 層,同樣,View 層的點擊等事件,通過 P 層去通知 M 層去處理。
如下圖所示:
MVP 模式應用實戰
蘇寧+App 項目結構
蘇寧+App 項目結構圖如下:
目前 App 整體項目架構如圖中所示,各個層次的介紹如下:
- 前端界面層:界面相關布局,如各種 activity/fragment 類。
- 業務邏輯層:業務邏輯相關,如各種 Presenter 類。
- 數據層:數據相關,包括數據存儲,獲取,如各種 Model 類。
- 運行服務層:伴隨應用生命周期自動初始化,自動銷毀,提供一系列服務給其他業務模塊調用,如各種 Service 類。
- 業務框架層:針對當前 App 跟業務有耦合度的公共方法,組件抽取。
- 基礎框架層:跟業務無關的底層組件,可以給多個 App 同時使用。
- 系統層:Android 系統底層。
通過上面的架構圖可以很直觀的看出,我們日常業務功能迭代的時候,主要修改或者新增的代碼都在前面三層,這里主要講前面三層的使用規范。
目錄結構
下圖為使用 MVP 模式時,購物車確認訂單頁面的目錄結構:
- model—數據處理。
- presenter—業務處理。
- task—網絡請求。
- ui—頁面。
- util—當前模塊公共類。
- view—頁面刷新回調接口。
總體邏輯設計
如下圖,為購物車 2 界面,下面將圍繞該界面來講解如何用 MVP 實現具體業務功能。
為了更加直觀看到 MVP 在當前業務中的使用,我們畫了類圖跟時序圖,通過類圖我們可以清楚類的設計,如下所示:
通過下面的時序圖,我們可以很清楚的看到調用關系:
通過上面兩張圖,我們可以看到 MVP 在當前業務中對應的角色以及調用關系,下面深入代碼層面繼續講解。
代碼實現
M 層(model)
項目中很多網絡請求是重復的,比如很多頁面都會用到店鋪信息接口,如果每個頁面都要在不同 Model 寫一遍,那么復用性很弱。
所以跟 Google 在 Github 發布的 MVPDemo 不同,我們項目中每個網絡接口都單獨寫成一個 Task,以確認訂單頁面為例:
- Model 層定義模型抽象類(PSCShopCart2DataSource)。
- 然后具體實現類(PSCShopCart2Repository)里面調用 Task,發送網絡請求。
代碼如下:
IView
MVP 模式中,M 層跟 V 層不能直接通信,數據是通過 Presenter 層接口回調到 V 層中。一般情況下,IView 里面的接口就對應 V 層的功能。
這邊會有人覺得特別復雜的場景會出現很多接口的情況,當然如果真出現這種情況,該合并的接口還是要合并,到 Activity 中做簡單的處理也是可以的。
實際開發中一定不能被框架限制,不管什么模式都是為了業務正常迭代。
代碼如下:
P 層(presenter)
原先雜糅在 activity/fragment 里面的業務邏輯移到 Presenter中,同時 Presenter 做為 M 和 V 之間交互的橋梁。
由于 Activity 跟 Fragment 生命周期不同,會影響一些彈出框關閉的時機,所以項目中,Activity 跟 Fragment 分別定義了一套基礎業務抽象類。
這邊以 Activity 基礎業務抽象類來演示,所有的 Activity 中用到的 Presenter 都繼承 PSCBaseActivityPresenter:
PSCActivityNetTask 主要做網絡任務監聽并回調到 Presenter 中,還會設置生命周期監聽,用于顯示加載框。
Presenter 接受到網絡回調后,根據接口返回的數據做業務處理,成功或者失敗分別通過接口回調到 View 層,刷新界面。
V 層(view)
相信大多數 App 都會有 baseActivity 作為基類,將 Activity 公共部分抽取出來進行封裝。
蘇寧的基類叫做 SuningActivity/SuningFragment,每個界面都需要把 View 跟 Presenter 綁定/解綁,這些都可以放到基類中。
然后定義 protected abstract TcreatePresenter();將創建 Presenter 步驟交給子類實現。
代碼量比較大,這邊做了刪減,僅保留 MVP 相關的代碼,如下:
Activity 實現上面定義的 IView,實現數據的接收,同時會在當前類中創建 Presenter,通過 Presenter 方法調用 Model 中的網絡請求。
總結
以上內容就是我們對于 MVP 架構的理解,并在蘇寧+項目中實戰后分享給大家。
MVC、MVP、MVVM 不管何種模式,都可以實現功能,選擇相應模式的時候,要看相對于目前業務來說的,何種模式能夠封裝變化,讓各模塊解耦,實現獨立變化,減少日后的維護工作和暗藏的風險。
當然我們也不能陷入模式的陷阱,為了使用模式而去套模式。沒有好的框架,只有適合的框架,如果大家發現我們當前項目中對于 MVP 的使用不對或者不完善的地方,歡迎提出來,我們一起探討。
曹銀飛,蘇寧云商 IT 總部 Android 技術專家,擁有多年 Android 研發和管理經驗。曾就職于聯創、騰訊等大型互聯網公司,現負責蘇寧易購 Android 開發部產品研發與技術管理工作,在 Android 項目架構設計,性能優化,團隊管理上有多年的實戰經驗。現致力于打造蘇寧智慧零售相關 App,希望將蘇寧的零售技術能力發揮到極致。
【51CTO原創稿件,合作站點轉載請注明原文作者和出處為51CTO.com】