標籤:
前言
關於架構的文章,博主很早就想寫了,雖說最近比較流行MVVM,但是MVP以及MVC也沒有過時之說,最主要還是要根據業務來選擇合適的架構。當然現在寫MVP的文章很多,也有很多好的文章,但是大多數看完後還是一頭霧水,用最少的文字表述清楚是我一貫的風格(這裡小小的裝逼一下),所以還是自己總結比較靠譜。
1.回顧MVC
講到MVP前我們有必要回顧下MVC,MVC(Model-View-Controller,模型-視圖-控制器)模式是80年代Smalltalk-80出現的一種軟體設計模式,後來得到了廣泛的應用,用一種商務邏輯、資料、介面顯示分離的方法組織代碼,在改進和個人化定製介面及使用者互動的同時,不需要重新編寫商務邏輯。
android的MVC
Android中介面部分也可以採用了MVC架構,MVC的角色定義分別為:
模型層(Model)
我們針對業務模型,建立的資料結構和相關的類,就可以理解為Model,Model是與View無關,而與業務相關的。
視圖層(View)
一般採用xml檔案或者java代碼進行介面的描述,也可以使用javascript+html等的方式作為view層。
控制層(controller)
android的控制層通常在acitvity、Fragment或者由它們控制的其他業務類中。
android的MVC缺點
在Android開發中,Activity並不是一個標準的MVC模式中的Controller,它的首要職責是載入應用的布局和初始化使用者介面,並接受並處理來自使用者的操作請求,進而作出響應。隨著介面及其邏輯的複雜度不斷提升,Activity類的職責不斷增加,以致變得龐大臃腫。
2.什麼是MVP
MVP(Model View Presenter)是MVC的演化版本,MVP的角色定義分別為:
Presenter
作為View和Model的溝通的橋樑,它從Model層檢索資料後返回給View層,使得View和Model之間沒有耦合。
Model
主要提供資料的存取功能。Presenter需要通過Model層來儲存、擷取資料。
View
負責處理使用者事件和視圖部分的展示。在Android中,它可能是Activity、Fragment類或者是某個View控制項。
在MVP裡,Presenter完全把Model和View進行了分離,主要的程式邏輯在Presenter裡實現。而且,Presenter與具體的View是沒有直接關聯的,而是通過定義好的介面進行互動,從而使得在變更View時候可以保持Presenter的不變。 View只應該有簡單的Set/Get的方法,使用者輸入和設定介面顯示的內容,除此就不應該有更多的內容,絕不容許直接存取Model,這就是與MVC很大的不同之處。
3.使用MVP
這裡我們舉個例子,通過網路擷取文章的標題和內容並顯示在介面上,訪問網路的內容和Android網路編程(三)Volley用法全解析這篇文章所採用的資料是一樣的,Json資料格式請點擊這裡。
訪問網路資料用的是OkHttpFinal,包目錄如所示:
實現Model
首先我們要建立bean檔案,這裡帖上部分代碼:
public class ArticleInfo { private String desc; private String status; private List<detail> detail = new ArrayList<detail>(); public List<ArticleInfo.detail> getDetail() { return detail; } public void setDetail(List<ArticleInfo.detail> detail) { this.detail = detail; } ...省略 public class detail { private String title; private String article_url; private String my_abstract; private String article_type; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; }...省略}
接下來是擷取文章的Model介面類,這個介面用來定義如何擷取資料:
public interface ArticleModel { void getArtcle(OnArticleListener onArticleListener);}
裡面有一個回調監聽介面,裡面定義了網路訪問回調的各種狀態:
public interface OnArticleListener { void onSuccess(ArticleInfo articleInfo); void onStart(); void onFailed(); void onFinish();}
接下來我們寫ArticleModel的實作類別用來擷取資料:
public class ArticleModelImpl implements ArticleModel { @Override public void getArtcle(final OnArticleListener onArticleListener) { HttpRequest.post("http://api.1-blog.com/biz/bizserver/article/list.do",new BaseHttpRequestCallback<ArticleInfo>(){ @Override protected void onSuccess(ArticleInfo articleInfo) { super.onStart(); onArticleListener.onSuccess(articleInfo); } @Override public void onStart() { super.onStart(); onArticleListener.onStart(); } @Override public void onFailure(int errorCode, String msg) { super.onFailure(errorCode, msg); onArticleListener.onFailed(); } @Override public void onFinish() { super.onFinish(); onArticleListener.onFinish(); } }); }}
通過OkHttpFinal來擷取資料,同時在回呼函數中調用自己定義的回呼函數。
實現Presenter
首先定義ArticlePresenter介面:
public interface ArticlePresenter { void getArticle();}
實現ArticlePresenter介面:
public class ArticlePresenterImpl implements ArticlePresenter, OnArticleListener { private ArticleView mArticleView; private ArticleModel mArticleModel; public ArticlePresenterImpl(ArticleView mArticleView) { this.mArticleView = mArticleView; mArticleModel = new ArticleModelImpl(); } @Override public void getArticle() { mArticleModel.getArtcle(this); } @Override public void onSuccess(ArticleInfo articleInfo) { mArticleView.setArticleInfo(articleInfo); } @Override public void onStart() { mArticleView.showLoading(); } @Override public void onFailed() { mArticleView.showError(); } @Override public void onFinish() { mArticleView.hideLoading(); }}
很明顯ArticlePresenterImpl 中含有ArticleModel 和ArticleView的執行個體(後面會講),通過實現OnArticleListener介面並調用ArticleModel 來擷取資料並回調給自身,最後通過ArticleView來和Activity進行互動,來更改介面。這回我們應該明白了,Presenter就是一個中間人的角色,他通過Model來獲得並儲存資料,然後在通過View來更新介面。這期間通過定義介面使得View和Model沒有任何互動。最後來看看View層的實現:
實現View
ArticleView用來定義介面互動的方法:
public interface ArticleView { void setArticleInfo(ArticleInfo articleInfo); void showLoading(); void hideLoading(); void showError();}
我們在Activity中來調用ArticlePresenterImpl:
public class MainActivity extends BaseActivity implements ArticleView{ private Button bt_getarticle; private TextView tv_article_title; private TextView tv_article_content; private ArticlePresenter mArticlePresenter; private Dialog mDialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { mArticlePresenter=new ArticlePresenterImpl(this); mDialog=new ProgressDialog(this); mDialog.setTitle("擷取資料中"); bt_getarticle = findView(R.id.bt_getarticle); tv_article_title = findView(R.id.tv_article_title); tv_article_content = findView(R.id.tv_article_content); bt_getarticle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mArticlePresenter.getArticle(); } }); } @Override public void setArticleInfo(ArticleInfo articleInfo) { if(null!=articleInfo) { List<ArticleInfo.detail> list = articleInfo.getDetail(); if(null!=list&&list.size()>1) tv_article_title.setText(list.get(1).getTitle()); tv_article_content.setText(list.get(1).getMy_abstract()); } } @Override public void showLoading() { mDialog.show(); } @Override public void hideLoading() { if(mDialog.isShowing()) { mDialog.dismiss(); } } @Override public void showError() { Toast.makeText(getApplicationContext(),"網路出錯",Toast.LENGTH_SHORT).show(); }}
需要注意的是MainActivity實現了ArticleView介面,用來接收回調更新介面,很明顯MainActivity並沒有做其他與介面無關的事情。
4.MVP的優缺點
優點
- 降低耦合度,實現了Model和View真正的完全分離。
- 模組職責劃分明顯,層次清晰。
- Presenter可以複用,一個Presenter可以用於多個View,而不需要更改Presenter的邏輯(當然是在View的改動不影響商務邏輯的前提下)。
- 如果我們把邏輯放在Presenter中,那麼我們就可以脫離使用者介面來測試這些邏輯(單元測試)。
缺點
- 額外的代碼複雜度及學習成本。
- 如果Presenter過多地與特定的視圖的聯絡過於緊密,一旦視圖需要變更,那麼Presenter也需要變更了。
5.總結
好了,MVP的例子就講到這,其實還有很多種方式來實現MVP,在這裡我也只是講了一個最基礎的方式,但是萬變不離其中。簡要總結MVP三者之間的關係是:View和Model之間沒有聯絡,View通過介面與Presenter進行互動,Model不主動和Presenter聯絡,被動的等著Presenter來調用其介面,Presenter通過介面和View/Model來聯絡。
github源碼下載
參考資料:
Android中的MVP
Android App的設計架構:MVC,MVP,MVVM與架構經驗談
Android開發中的MVP架構詳解
淺談 MVP in Android
使用MVP模式重構代碼
MVP模式是你的救命稻草嗎?
Android MVP 詳解(上)
MVP模式的優缺點
Android MVP 執行個體運用
Android架構(一)MVP全解析