對於MVP(Model View Presenter),大多數人都能說出一二:「MVC的演化版本」,「讓Model和View徹底解耦」等等。本篇博文僅是爲了作下記錄,提出一些本身的見解,和幫助你們如何針對一個Activity頁面去編寫針對MVP風格的代碼。html
對於MVP,個人心裏有一個問題:java
爲什麼這個模式出來後,就能被廣大的Android的程序員接受呢?android
問了些程序員,他們對於MVP的廣泛的認識是:「代碼很清晰,不過增長了不少類」。我在第一次看到MVP的時候,看了一個demo,看完之後以爲很是nice(可是回過頭來,本身想個例子寫,就頭疼寫不出來,固然這在後文會說)。nice的緣由仍是由於,這個模式的確讓代碼的清晰度有了很大的提高。git
那麼,提高通常都是對比出來的,回顧下,沒有應用MVP的代碼結構。不少人說明顯是MVC麼:程序員
看起來的確像那麼回事,可是細細的想一想這個View對應於佈局文件,其實能作的事情特別少,實際上關於該佈局文件中的數據綁定的操做,事件處理的代碼都在Activity中,形成了Activity既像View又像Controller(固然了Data-Binder的出現,可能會讓View更像View吧)。這可能也就是爲什麼,在該文中有一句這樣的話:github
Most of the modern Android applications just use View-Model architecture,everything is connected with Activity.服務器
而當將架構改成MVP之後,Presenter的出現,將Actvity視爲View層,Presenter負責完成View層與Model層的交互。如今是這樣的:架構
ok,先簡單瞭解下,文中會有例子到時候能夠直觀的感覺下。mvc
小總結下,也就是說,之因此讓人以爲耳目一新,是由於此次的跳躍是從並不標準的MVC
到MVP
的一個轉變,減小了Activity的職責,簡化了Activity中的代碼,將複雜的邏輯代碼提取到了Presenter中進行處理。與之對應的好處就是,耦合度更低,更方便的進行測試。借用兩張圖(出自:該文),表明上述的轉變:app
轉變爲:
ok,上面說了一堆理論,下面咱們仍是須要看一看MVC與MVP的一個區別,請看下圖(來自:本文):
其實最明顯的區別就是,MVC中是容許Model和View進行交互的,而MVP中很明顯,Model與View之間的交互由Presenter完成。還有一點就是Presenter與View之間的交互是經過接口的(代碼中會體現)。
還有一堆概念性的東西,以及優勢就略了,有興趣自行百度。下面仍是經過一些簡單的需求來展現如何編寫MVP的demo。
效果圖:
看到這樣的效果,先看下完工後的項目結構:
ok,接下來開始一步一步的編寫思路。
首先實體類User不用考慮這個確定有,其次從效果圖能夠看到至少有一個業務方法login(),這兩點沒什麼難度,咱們首先完成:
package com.zhy.blogcodes.mvp.bean; /** * Created by zhy on 15/6/18. */ public class User { private String username ; private String password ; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
package com.zhy.blogcodes.mvp.biz; /** * Created by zhy on 15/6/19. */ public interface IUserBiz { public void login(String username, String password, OnLoginListener loginListener); }
package com.zhy.blogcodes.mvp.biz; import com.zhy.blogcodes.mvp.bean.User; /** * Created by zhy on 15/6/19. */ public class UserBiz implements IUserBiz { @Override public void login(final String username, final String password, final OnLoginListener loginListener) { //模擬子線程耗時操做 new Thread() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //模擬登陸成功 if ("zhy".equals(username) && "123".equals(password)) { User user = new User(); user.setUsername(username); user.setPassword(password); loginListener.loginSuccess(user); } else { loginListener.loginFailed(); } } }.start(); } }
package com.zhy.blogcodes.mvp.biz; import com.zhy.blogcodes.mvp.bean.User; /** * Created by zhy on 15/6/19. */ public interface OnLoginListener { void loginSuccess(User user); void loginFailed(); }
實體類不用說,至於業務類,咱們抽取了一個接口,一個實現類這也很常見~~login方法,通常確定是鏈接服務器的,是個耗時操做,因此咱們開闢了子線程,Thread.sleep(2000)模擬了耗時,因爲是耗時操做,因此咱們經過一個回調接口來通知登陸的狀態。
其實這裏仍是比較好寫的,由於和傳統寫法沒區別。
上面咱們說過,Presenter與View交互是經過接口。因此咱們這裏須要定義一個ILoginView
,難點就在於應該有哪些方法,咱們看一眼效果圖:
能夠看到咱們有兩個按鈕,一個是login,一個是clear;
login說明了要有用戶名、密碼,那麼對應兩個方法:
String getUserName();
String getPassword();
再者login是個耗時操做,咱們須要給用戶一個友好的提示,通常就是操做ProgressBar,因此再兩個:
void showLoading(); void hideLoading();
login固然存在登陸成功與失敗的處理,咱們主要當作功咱們是跳轉Activity,而失敗多是去給個提醒:
void toMainActivity(User user); void showFailedError();
ok,login這個方法咱們分析完了~~還剩個clear那就簡單了:
void clearUserName(); void clearPassword();
綜上,接口完整爲:
package com.zhy.blogcodes.mvp.view; import com.zhy.blogcodes.mvp.bean.User; /** * Created by zhy on 15/6/19. */ public interface IUserLoginView { String getUserName(); String getPassword(); void clearUserName(); void clearPassword(); void showLoading(); void hideLoading(); void toMainActivity(User user); void showFailedError(); }
有了接口,實現就太好寫了~~~
總結下,對於View的接口,去觀察功能上的操做,而後考慮:
下面貼一下咱們的View的實現類,哈,其實就是Activity,文章開始就說過,MVP中的View其實就是Activity。
package com.zhy.blogcodes.mvp; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.Toast; import com.zhy.blogcodes.R; import com.zhy.blogcodes.mvp.bean.User; import com.zhy.blogcodes.mvp.presenter.UserLoginPresenter; import com.zhy.blogcodes.mvp.view.IUserLoginView; public class UserLoginActivity extends ActionBarActivity implements IUserLoginView { private EditText mEtUsername, mEtPassword; private Button mBtnLogin, mBtnClear; private ProgressBar mPbLoading; private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_user_login); initViews(); } private void initViews() { mEtUsername = (EditText) findViewById(R.id.id_et_username); mEtPassword = (EditText) findViewById(R.id.id_et_password); mBtnClear = (Button) findViewById(R.id.id_btn_clear); mBtnLogin = (Button) findViewById(R.id.id_btn_login); mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading); mBtnLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mUserLoginPresenter.login(); } }); mBtnClear.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mUserLoginPresenter.clear(); } }); } @Override public String getUserName() { return mEtUsername.getText().toString(); } @Override public String getPassword() { return mEtPassword.getText().toString(); } @Override public void clearUserName() { mEtUsername.setText(""); } @Override public void clearPassword() { mEtPassword.setText(""); } @Override public void showLoading() { mPbLoading.setVisibility(View.VISIBLE); } @Override public void hideLoading() { mPbLoading.setVisibility(View.GONE); } @Override public void toMainActivity(User user) { Toast.makeText(this, user.getUsername() + " login success , to MainActivity", Toast.LENGTH_SHORT).show(); } @Override public void showFailedError() { Toast.makeText(this, "login failed", Toast.LENGTH_SHORT).show(); } }
對於在Activity中實現咱們上述定義的接口,是一件很容易的事,畢竟接口引導咱們去完成。
最後看咱們的Presenter。
Presenter是用做Model和View之間交互的橋樑,那麼應該有什麼方法呢?
其實也是主要看該功能有什麼操做,好比本例,兩個操做:login和clear。
package com.zhy.blogcodes.mvp.presenter; import android.os.Handler; import com.zhy.blogcodes.mvp.bean.User; import com.zhy.blogcodes.mvp.biz.IUserBiz; import com.zhy.blogcodes.mvp.biz.OnLoginListener; import com.zhy.blogcodes.mvp.biz.UserBiz; import com.zhy.blogcodes.mvp.view.IUserLoginView; /** * Created by zhy on 15/6/19. */ public class UserLoginPresenter { private IUserBiz userBiz; private IUserLoginView userLoginView; private Handler mHandler = new Handler(); public UserLoginPresenter(IUserLoginView userLoginView) { this.userLoginView = userLoginView; this.userBiz = new UserBiz(); } public void login() { userLoginView.showLoading(); userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() { @Override public void loginSuccess(final User user) { //須要在UI線程執行 mHandler.post(new Runnable() { @Override public void run() { userLoginView.toMainActivity(user); userLoginView.hideLoading(); } }); } @Override public void loginFailed() { //須要在UI線程執行 mHandler.post(new Runnable() { @Override public void run() { userLoginView.showFailedError(); userLoginView.hideLoading(); } }); } }); } public void clear() { userLoginView.clearUserName(); userLoginView.clearPassword(); } }
注意上述代碼,咱們的presenter完成兩者的交互,那麼確定須要兩者的實現類。大體就是從View中獲取須要的參數,交給Model去執行業務方法,執行的過程當中須要的反饋,以及結果,再讓View進行作對應的顯示。
本文轉載自:淺談 MVP in Android