Android MVP模式詳解
一、MVP概述
MVP,全稱 Model-View-Presenter,即模型-檢視-層現器。
提到MVP,就必須要先介紹一下它的前輩MVC,因為MVP正是基於MVC的基礎發展而來的。兩個之間的關係也是源遠流長。
MVC,全稱Model-View-Controller,即模型-檢視-控制器。具體如下:
View:對應於佈局檔案
Model:業務邏輯和實體模型
Controllor:對應於Activity
但是View對應於佈局檔案,其實能做的事情特別少,實際上關於該佈局檔案中的資料繫結的操作,事件處理的程式碼都在Activity中,造成了Activity既像View又像Controller,使得Activity變得臃腫。
而當將架構改為MVP以後,Presenter的出現,將Actvity視為View層,Presenter負責完成View層與Model層的互動。現在是這樣的:
View 對應於Activity,負責View的繪製以及與使用者互動
Model 依然是業務邏輯和實體模型
Presenter 負責完成View於Model間的互動
下面兩幅圖通過資料與檢視之間的互動清楚地展示了這種變化:

img
MVC模式下實際上就是Activty與Model之間互動,View完全獨立出來了。

img
MVP模式通過Presenter實現資料和檢視之間的互動,簡化了Activity的職責。同時即避免了View和Model的直接聯絡,又通過Presenter實現兩者之間的溝通。
總結:MVP模式減少了Activity的職責,簡化了Activity中的程式碼,將複雜的邏輯程式碼提取到了Presenter中進行處理,模組職責劃分明顯,層次清晰。與之對應的好處就是,耦合度更低,更方便的進行測試。
MVC和MVP的區別

img
MVC中是允許Model和View進行互動的,而MVP中很明顯,Model與View之間的互動由Presenter完成。還有一點就是Presenter與View之間的互動是通過介面的。
還有一點注意: MVC中V對應的是佈局檔案,MVP中V對應的是Activity。
二、MVP的簡單使用
大多數MVP模式的示例都使用登入案例進行介紹。因為簡單方便,同時能提現出MVP的特點。今天我們也以此例進行學習。 使用MVP的好處之一就是模組職責劃分明顯,層次清晰。 該例的結構圖即可展現此優點。

img
1.Model層
在本例中,M0del層負責對從登入頁面獲取地帳號密碼進行驗證(一般需要請求伺服器進行驗證,本例直接模擬這一過程)。 從上圖的包結構圖中可以看出,Model層包含內容:
①實體類bean
②介面,表示Model層所要執行的業務邏輯
③介面實現類,具體實現業務邏輯,包含的一些主要方法
下面以程式碼的形式一一展開。
①實體類bean
public class User { private String password; private String username; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String toString() { return "User{" + "password='" + password + '\'' + ", username='" + username + '\'' + '}'; } }
封裝了使用者名稱、密碼,方便資料傳遞。
②介面
public interface LoginModel { void login(User user, OnLoginFinishedListener listener); }
其中OnLoginFinishedListener 是presenter層的介面,方便實現回撥presenter,通知presenter業務邏輯的返回結果,具體在presenter層介紹。
③介面實現類
public class LoginModelImpl implements LoginModel { @Override public void login(User user, final OnLoginFinishedListener listener) { final String username = user.getUsername(); final String password = user.getPassword(); new Handler().postDelayed(new Runnable() { @Override public void run() { boolean error = false; if (TextUtils.isEmpty(username)){ listener.onUsernameError();//model層裡面回撥listener error = true; } if (TextUtils.isEmpty(password)){ listener.onPasswordError(); error = true; } if (!error){ listener.onSuccess(); } } }, 2000); } }
實現Model層邏輯:延時模擬登陸(2s),如果使用者名稱或者密碼為空則登陸失敗,否則登陸成功。
2.View層
檢視:將Modle層請求的資料呈現給使用者。一般的檢視都只是包含使用者介面(UI),而不包含介面邏輯,介面邏輯由Presenter來實現。
從上圖的包結構圖中可以看出,View包含內容:
①介面,上面我們說過Presenter與View互動是通過介面。其中介面中方法的定義是根據Activity使用者互動需要展示的控制元件確定的。
②介面實現類,將上述定義的介面中的方法在Activity中對應實現具體操作。
下面以程式碼的形式一一展開。
①介面
public interface LoginView { //login是個耗時操作,我們需要給使用者一個友好的提示,一般就是操作ProgressBar void showProgress(); void hideProgress(); //login當然存在登入成功與失敗的處理,失敗給出提示 void setUsernameError(); void setPasswordError(); //login成功,也給個提示 void showSuccess(); }
上述5個方法都是presenter根據model層返回結果需要view執行的對應的操作。
②介面實現類
即對應的登入的Activity,需要實現LoginView介面。
public class LoginActivity extends AppCompatActivity implements LoginView, View.OnClickListener { private ProgressBar progressBar; private EditText username; private EditText password; private LoginPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); progressBar = (ProgressBar) findViewById(R.id.progress); username = (EditText) findViewById(R.id.username); password = (EditText) findViewById(R.id.password); findViewById(R.id.button).setOnClickListener(this); //建立一個presenter物件,當點選登入按鈕時,讓presenter去呼叫model層的login()方法,驗證帳號密碼 presenter = new LoginPresenterImpl(this); } @Override protected void onDestroy() { presenter.onDestroy(); super.onDestroy(); } @Override public void showProgress() { progressBar.setVisibility(View.VISIBLE); } @Override public void hideProgress() { progressBar.setVisibility(View.GONE); } @Override public void setUsernameError() { username.setError(getString(R.string.username_error)); } @Override public void setPasswordError() { password.setError(getString(R.string.password_error)); } @Override public void showSuccess() { progressBar.setVisibility(View.GONE); Toast.makeText(this,"login success",Toast.LENGTH_SHORT).show(); } @Override public void onClick(View v) { User user = new User(); user.setPassword(password.getText().toString()); user.setUsername(username.getText().toString()); presenter.validateCredentials(user); } }
View層實現Presenter層需要呼叫的控制元件操作,方便Presenter層根據Model層返回的結果進行操作View層進行對應的顯示。
3.Presenter層
Presenter是用作Model和View之間互動的橋樑。 從上圖的包結構圖中可以看出,Presenter包含內容:
①介面,包含Presenter需要進行Model和View之間互動邏輯的介面,以及上面提到的Model層資料請求完成後回撥的介面。
②介面實現類,即實現具體的Presenter類邏輯。
下面以程式碼的形式一一展開。
①介面
public interface OnLoginFinishedListener { void onUsernameError(); void onPasswordError(); void onSuccess(); }
當Model層得到請求的結果,需要回調Presenter層,讓Presenter層呼叫View層的介面方法。
public interface LoginPresenter { void validateCredentials(User user); void onDestroy(); }
登陸的Presenter 的介面,實現類為LoginPresenterImpl,完成登陸的驗證,以及銷燬當前view。
②介面實現類
public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener { private LoginView loginView; private LoginModel loginModel; public LoginPresenterImpl(LoginView loginView) { this.loginView = loginView; this.loginModel = new LoginModelImpl(); } @Override public void validateCredentials(User user) { if (loginView != null) { loginView.showProgress(); } loginModel.login(user, this); } @Override public void onDestroy() { loginView = null; } @Override public void onUsernameError() { if (loginView != null) { loginView.setUsernameError(); loginView.hideProgress(); } } @Override public void onPasswordError() { if (loginView != null) { loginView.setPasswordError(); loginView.hideProgress(); } } @Override public void onSuccess() { if (loginView != null) { loginView.showSuccess(); } } }
由於presenter完成二者的互動,那麼肯定需要二者的實現類(通過傳入引數,或者new)。
presenter裡面有個OnLoginFinishedListener, 其在Presenter層實現,給Model層回撥,更改View層的狀態, 確保 Model層不直接操作View層。
示例展示:

img
三、總結
MVP模式的整個核心流程:
View與Model並不直接互動,而是使用Presenter作為View與Model之間的橋樑。其中Presenter中同時持有View層的Interface的引用以及Model層的引用,而View層持有Presenter層引用。當View層某個介面需要展示某些資料的時候,首先會呼叫Presenter層的引用,然後Presenter層會呼叫Model層請求資料,當Model層資料載入成功之後會呼叫Presenter層的回撥方法通知Presenter層資料載入情況,最後Presenter層再呼叫View層的介面將載入後的資料展示給使用者。

img