編輯:關於Android編程
關於架構的文章,博主很早就想寫了,雖說最近比較流行MVVM,但是MVP以及MVC也沒有過時之說,最主要還是要根據業務來選擇合適的架構。當然現在寫MVP的文章很多,也有很多好的文章,但是大多數看完後還是一頭霧水,用最少的文字表述清楚是我一貫的風格(這裡小小的裝逼一下),所以還是自己總結比較靠譜。
講到MVP前我們有必要回顧下MVC,MVC(Model-View-Controller,模型-視圖-控制器)模式是80年代Smalltalk-80出現的一種軟件設計模式,後來得到了廣泛的應用,用一種業務邏輯、數據、界面顯示分離的方法組織代碼,在改進和個性化定制界面及用戶交互的同時,不需要重新編寫業務邏輯。
Android中界面部分也可以采用了MVC框架,MVC的角色定義分別為:
模型層(Model)
我們針對業務模型,建立的數據結構和相關的類,就可以理解為Model,Model是與View無關,而與業務相關的。
視圖層(View)
一般采用xml文件或者java代碼進行界面的描述,也可以使用javascript+html等的方式作為view層。
控制層(controller)
android的控制層通常在acitvity、Fragment或者由它們控制的其他業務類中。
在Android開發中,Activity並不是一個標准的MVC模式中的Controller,它的首要職責是加載應用的布局和初始化用戶界面,並接受並處理來自用戶的操作請求,進而作出響應。隨著界面及其邏輯的復雜度不斷提升,Activity類的職責不斷增加,以致變得龐大臃腫。
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很大的不同之處。
這裡我們舉個例子,通過網絡獲取文章的標題和內容並顯示在界面上,訪問網絡的內容和Android網絡編程(三)Volley用法全解析這篇文章所采用的數據是一樣的,Json數據格式請點擊這裡。
訪問網絡數據用的是OkHttpFinal,包目錄如下圖所示:
首先我們要創建bean文件,這裡帖上部分代碼:
public class ArticleInfo { private String desc; private String status; private Listdetail = new ArrayList (); public List getDetail() { return detail; } public void setDetail(List 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(){ @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來獲取數據,同時在回調函數中調用自己定義的回調函數。
首先定義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層的實現:
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 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並沒有做其他與界面無關的事情。
優點
降低耦合度,實現了Model和View真正的完全分離。 模塊職責劃分明顯,層次清晰。 Presenter可以復用,一個Presenter可以用於多個View,而不需要更改Presenter的邏輯(當然是在View的改動不影響業務邏輯的前提下)。 如果我們把邏輯放在Presenter中,那麼我們就可以脫離用戶接口來測試這些邏輯(單元測試)。缺點
額外的代碼復雜度及學習成本。 如果Presenter過多地與特定的視圖的聯系過於緊密,一旦視圖需要變更,那麼Presenter也需要變更了。好了,MVP的例子就講到這,其實還有很多種方式來實現MVP,在這裡我也只是講了一個最基礎的方式,但是萬變不離其中。簡要總結MVP三者之間的關系是:View和Model之間沒有聯系,View通過接口與Presenter進行交互,Model不主動和Presenter聯系,被動的等著Presenter來調用其接口,Presenter通過接口和View/Model來聯系。
github源碼下載
管理Activity(Fragment、dialogFragment)的生命周期需要在build.gradle中加入compile 'com.trello:rxl
在安卓開發中,會碰到選開始日期和結束日期的問題。特別是在使用Pad時,如果彈出一個Dialog,能夠同時選擇開始日期和結束日期,那將是極好的。我在開發中在DatePick
之前寫過一篇Eclipse制作.so的文章,使用的是GNUstep模擬Linux環境,過程現在看來是想相當麻煩,後來發現一個簡單的方法就是通過項目右鍵添加Native S
行為變更Android N 除了提供諸多新特性和功能外,還對系統和 API 行為做出了各種變更。本文重點介紹您應該了解並在開發應用時加以考慮的一些重要變更。如果您之前發布