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