Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 項目框架 使用MVP開發

Android 項目框架 使用MVP開發

編輯:關於Android編程

前言

在Android中使用 MVP 來開發已經出來很久了,剛好Google又出了一系列的architecture samples,在此就整理一下對於MVP的認知和實踐總結,這篇文章會隨著使用經驗的豐富而不斷更新。

1. 介紹MVC

在沒有使用MVP開發之前,我們一直使用的都是MVC模式,其實也不算的MVC,一般我們聽到的都是Android中的Activity既是View,又是Controller,即Activity既負責View的顯示,又負責處理業務邏輯,這是我們一般聽到的,但其實我們的Activity還負責了數據的保存和讀取。

當業務邏輯簡單,僅僅只是獲取數據、展示數據時,我們的代碼是這樣:
Model-View

但其實考慮數據的存儲、業務邏輯等等,我們是這樣:
God_Object
所有東西都連在一起,太復雜以至於成了一個面了,這個面就是這個God Object:Activity。

想想看,我們是不是把所有東西都寫在這個上帝類中了:網絡請求、解析Json、處理數據、展示數據、緩存數據等等,全部都在Activity中,盡管有些東西已經封裝的很好,但依舊都和Activity扯在一起,基本上所有代碼都在Activity中,於是所謂的MVC,就叫做 massive view controller,超級大的ViewController。
這樣做盡管有好處,很容易看清楚業務邏輯,而且寫起來很方便、順手,不知不覺就這樣寫了。但是很明顯,隨著需求不斷加多,該類代碼就越寫越多,並且如果需求有改動的話,那改起來很費勁,而且這樣寫,不敢輕易重構,因為太復雜,動一點可能就會有bug。

那麼不想這麼做怎麼辦呢?於是就有了MVP。

2. 介紹MVP

典型的MVP就像這樣:
MVP
 

很明顯:

MVP中Presenter和View是相互交互的。 MVP拒絕View和Model直接交互,View和Model只能通過Presenter間接交互。這點是合理的,因為Android對UI的操作必須在 UIThread 上,而對Model的處理大多是異步任務。通過轉接一層,可以很方便處理線程問題。 Model和View是被動的,一切都由Presenter來主導

我們一般把 Activity/Fragment 當做View,那麼這時我們的Activity/Fragment中的代碼簡單多了,響應Presenter的指令對View進行操作,而這些操作是在IVIEW 接口中定義好的。這樣大部分代碼轉移到了Presenter中,而且一些業務代碼被封裝到了Model層去,這樣的代碼就會清晰很多,寫完一看,哇塞,太清晰了。而且很容易定位bug,畢竟代碼職責越清晰越不容易出錯。

那麼現在分開來介紹:

Model
為UI層提供的數據,或者保存UI層傳下來的數據。
Model 是用戶界面需要顯示數據的抽象,也可以理解為從業務數據(結果)那裡到用戶界面的抽象。
Model 應該封裝業務邏輯,不讓Presenter和View知道業務邏輯層 View
View is a layer that displays data and reacts to user actions.
View提供友好的交互、展示數據、響應用戶操作,並都轉發給Presenter來做具體的處理 Presenter
邏輯控制層,分發邏輯。處理著程序各種邏輯的分發,收到View層UI上的反饋命令、定時命令、系統命令等指令後分發處理邏輯交由業務層做具體的業務操作,然後將得到的 Model 給 View 顯示。

到這裡,關於MVP的基本概念講解完了,就是分三層寫代碼,此處建議使用MVP時分包時按照模塊分包,模塊比較多時,建一個module包,裝所有模塊,每個模塊再細分三層,這樣包很清晰、代碼也很清晰。

接下來看一些google的示例。

3. Google samples中的MVP

Google samples中的MVP(google MVP示例中這張圖真的很完美的闡述了一個簡單的app的架構):
MVP.png

Model層獲取數據時,使用DataRepository 分發管理,從Presenter處接收指令從RemoteDataSource 或者 LocalDataSource 中獲取數據,並回調給Presenter,Presenter再回調給View用於展示。

下面列出一些官方推薦 的點,這些都可以在代碼中找到:

Use Fragments as View. The separation between Activity and Fragment fits nicely with this implementation of MVP: the Activity is the overall controller that creates and connects views and presenters,which means the Activity is responsible for the creation of fragments and presenters.
使用Fragment作為View,Activity作為全局的Controller把presenter和view綁定起來。

Create a contract interface defining the view and the presenter,which contains the View interface and the presenter interface. Contracts are interfaces used to define the connection between views and presenters. This is a amazing concept.
創建 IContract 父類接口來管理同一個模塊下的 IViewIPresenter接口,這樣很清楚的能看清楚兩者的邏輯。

In general, the business logic lives in the presenter and relies on the view to do the Android UI work.
業務邏輯全部在Presenter層,依靠View層來觸發。

The view contains almost no logic: it converts the presenter’s commands to UI actions and listens to user actions, which are passed to the presenter.
View幾乎沒有任何邏輯,響應Presenter層的指令,並接收用戶操作傳給Prenster。當然很少很輕量的邏輯應該在這一層中處理(判空處理等),這一點應該要自己衡量。View應該是被動的。

4. MVP是一個方法論

我們應該很容易看到在MVP中僅僅有一些基本原則,並沒有一個固定的方式去實現MVP,都針對自家的業務需求、邏輯去更好的更合適的使用MVP,只要View和Model分離,代碼清晰,易於調試,易於測試,都OK。

MVP的一些指導性原則來約束實現:

Model與View不能直接通信,只能通過Presenter Presenter類似於中間人的角色進行協調和調度 Model和View是接口,Presenter持有的是一個Model接口和一個View接口 Model和View都應該是被動的,一切都由Presenter來主導 Model應該把與業務邏輯層的交互封裝掉,換句話說Presenter和View不應該知道業務邏輯層 View的邏輯應該盡可能的簡單,不應該有狀態。當事件發生時,調用Presenter來處理,並且不傳參數,Presenter處理時再調用View的方法來獲取。

知道了MVP是一個方法論,下面說說MVP的另一個實現方式。

5. MVP的另一種實現方式

MVP另一個實現方式(變種)就是不像一般的把Activity/Fragment作為View,而是把他們作為Presenter來使用,單獨把View用一個類來管理。

使用該種方式可以大大減少類的個數,因為是依靠 泛型 來解耦的,所以可以不用提供接口,當然提供了也行,我在實現過程中刪除了接口,同時把點擊事件的響應結果當成邏輯,放在Presenter中,這樣就不用傳來傳去的。(之前使用Fragment作為View時,點擊ListItem 需要修改已讀狀態,傳入到Presenter中修改,Presenter修改完後又調用View.showDetailUI()方法又傳回到View中,使用變種後這塊就大大減輕工作量了)

使用這種方式個人認為有點怪異,一是使用泛型加大了理解難度,寫著寫著就不知道怎麼調用到這塊了;二是刪除接口後感覺復用性降低了,邏輯也不能復用、View也不能復用,當然可以不刪接口,但是不刪接口和原來的MVP有什麼區別呢。恩,不過這點可能隨著使用MVP越深入會越能理解,Activity到底是View還是控制器,可能隨著項目的偏重不同,會適合不同的情況吧。

那麼說完了MVP變種,下面總結一下個人使用MVP的心得。

6. MVP心得體會

使用MVP開發,頓時感覺清爽很多,雖然多了很多代碼,但是瑕不掩瑜,讓代碼清爽很多。真的很棒,看了重構後的代碼,感覺自己之前真是活在噩夢裡一樣,終於不用把所有代碼寫在Activity裡或是Fragment裡了。下面就具體說說使用中的心得體會。

首先是方法論上:

首先分清除什麼是 分發業務邏輯、業務邏輯、視圖邏輯。
理解這三點對於設計接口、使用MVP寫項目、重構等有很大的幫助。視圖邏輯可以更好的寫View,分發業務邏輯用於Presenter,業務邏輯用於Model,當然一些業務邏輯是在Presenter中的。

然後看什麼是View,什麼是邏輯,哪些邏輯展示哪些View,哪些操作調用哪些邏輯,這會很好的幫助我們理清代碼邏輯、方便相互調用。

一個 View 可以有多個 Presenter,要用到什麼業務就加入什麼 Presenter,並且實現這個 Presenter 所需要的 View 接口即可,這就是簡單的復用邏輯,即Presenter是可復用的。

然後是代碼上:

IView接口裡的方法全部是UI相關,show()、hide()等,不應該出現邏輯相關。 IPresenter接口裡的方法應該全部是邏輯相關,進行分發,很明顯,要持有Model引用,方便對數據進行操作。不應該出現與Model相關的方法,比如LoadFromLocal,LoadFromRemote,這應該是DataRepository處理的事情。Presenter裡面只需要告訴Model說我需要這個數據,即LoadData,而不用管從哪Load數據。這裡面只需要負責 分發!!! Model中,對數據進行處理,所有的數據從這裡進出,並對數據進行處理。一般創建DataRepository類對LocalDataSource,RemoteDataSource進行管理分發,這三個類都實現IDataSource接口,保證邏輯一致。

最後一些個人體會:

在MVP裡面,越來越感到依賴注入的重要性。全部是依賴,全部需要注入,感覺依賴注入框架 Dagger2 是時候展現它強大的功能了。

回調太多。獲取數據時,從Presenter中開始callback,再到DataRepository中,再到具體的LocalDataSource或RemoteDataSource的callback,總共回調了三層,增長了鏈條。很明顯,解決這個問題需要用到 RxJava 了,可以省去很多代碼,變得簡潔是必然的。

Google samples 中的 IContract 接口管理類真的非常棒,管理了IPresenter和IView,如果可以的話,應該讓它也管理IDataSource 這個接口,非常清晰的一種方式。

盡管MVP實現了一定的分離,但相當於大量代碼從Activity中轉移到Presenter和Model中,盡管使用接口使代碼清晰很多,但避免不了難看和難以查找,隨著業務的不斷復雜,代碼依舊會越來越多。所以使用clean架構應該是最好。

最後不得不說確實方法數、類都增加了好多。但這也是必然的。解耦、方便測試。

7. 代碼實現

說了那麼說,show me the code.

那麼,自己寫的代碼全在GitHub上,全部以分支形式展現:

最基礎的分支,全部代碼在Activity中: branch_basic

MVP分支,使用MVP重構代碼:branch_mvp

MVP另一種實現方式(變種)分支:branch_mvp_variant

之後,肯定會嘗試clean架構再次重構。不過由於項目本身較小,所以當個練手的真的不錯。

 

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved