摘要: 在MVVM成熟以前MVP模式在Android上有被神化的趨勢,筆者曾經在商業項目中從零開始大規模採用過MVP模式對項目進行開發。在使用MVP模式進行開發的時候發現項目的結構模式對開發是有必定的影響的,在這裏筆者會對這一問題進行探討。但願經過這篇blog能讓讀者瞭解如何使用MVP模式搭建一個功能完善的MVP模式開發框架,避免一些筆者認爲比較嚴重的問題。git
在傳統的Android開發中,咱們通常是使用MVC模式進行開發的。
傳統MVC模式介紹:github
在Android開發中採用MVC模式的一個最大的弊端就是xml做爲View層視圖能力實在太弱,因此通常狀況下咱們都是經過Controller層來輔助處理一些視圖的。這樣的結果就致使Controller既做爲控制層的同時又承擔了View層的大部分功能,採用MVC模式每每會致使Activity和Fragment中的代碼很是複雜。咱們將Android中採用的MVC模式稱爲MV模式更加恰當。web
MVP模式介紹:服務器
MVP模式圖解框架
採用MVP模式的優點是:ide
採用MVP模式的缺點:post
綜上所述,在Android上採用MVP模式的優點是:大大優化代碼的維護性與拓展性的同時對代碼進行深度解耦,使各個層級的分工更加明晰。單元測試
先來看看一個簡單用mvp模式模擬登錄的demo,下面的示例代碼和其它簡單介紹MVP模式的代碼沒有太大區別。若是有了解過的同窗能夠直接跳過看下一章關於如何優化MVP模式的結構的文章。學習
下面咱們來看看在Android上用MVP模式實現簡單的登陸邏輯的方式:
從上面的代碼結構圖可看出,用MVP模式實現登錄模塊須要建立6個文件,分別是M、V、P接口文件和接口的對應實現。其中LoginActivity就是View層的具體實現。這樣的好處時Activity組件只須要負責處理UI相關邏輯就能夠了,而相關的業務邏輯所有抽象到Presenter層中處理。經過這種方式可以很好的避免傳統Android開發中的Activity/Fragment等UI組件既負責處理UI邏輯又處理業務邏輯的結果。
說了這麼多,最後咱們來看看代碼的實現吧。
ILoginModel
1
2
3
|
public interface ILoginModel {
void login(String name ,String password);
}
|
ILoginPresenter
1
2
3
4
5
6
|
public interface ILoginPresenter {
void loginToServer(String userName,String password);
void loginSucceed();
}
|
ILoginView
1
2
3
4
5
6
|
public interface ILoginView {
void showProgress(boolean enable);
void showLoginView();
}
|
上面是登錄模塊對應的MVP接口的具體設計,下面我來簡單介紹一下接口中的幾個方法:
ILoginModel.login(String name ,String password)
登錄方法,經過該方法向服務器發送登錄請求。ILoginPresenter. loginToServer (String name ,String password)
通知Model響應登錄事件。ILoginPresenter. loginSucceed()
當登錄事件完成時(成功/失敗),Model層要通知該方法登錄事件已完成。ILoginView. showProgress(boolean enable)
當Presenter層調用loginToServer (String name ,String password)
方法時,要經過該方法通知View層顯示加載動畫。ILoginView. showLoginView()
登錄成功時,Presenter層會經過該方法通知View層登錄已成功。下面咱們來看看這幾個接口的具體實現。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class LoginModel implements ILoginModel{
private ILoginPresenter presenter;
private Handler mHandler = new Handler();
public LoginModel(ILoginPresenter presenter) {
this.presenter = presenter;
}
public void login(String name ,String password) {
mHandler.postDelayed(
new Runnable() {
public void run() {
Log.d(
"LoginModel", "run: ");
presenter.loginSucceed();
}
},
2000);
}
}
|
上面的Model層實現了login(String name,Stringpassword)
登錄方法,該方法的具體實現邏輯是經過線程休眠2秒來模擬網絡登錄的過程,登錄成功後會經過LoginPresenter的loginSucceed()
方法來通知Presenter層登錄結果。實際開發中咱們須要根據具體的業務邏輯來實現該過程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class LoginPresenter implements ILoginPresenter{
private ILoginModel loginModel;
private ILoginView loginView;
public LoginPresenter(ILoginView loginView) {
this.loginView = loginView;
this.loginModel = new LoginModel(this);
}
public void loginToServer(String userName, String password) {
loginView.showProgress(
true);
loginModel.login(userName,password);
}
public void loginSucceed() {
loginView.showProgress(
false);
loginView.showLoginView();
}
}
|
從上面代碼能夠看出LoginPresenter的實現邏輯很簡單,首先在構造方法中獲取ILoginView對象並撞見ILoginModel對象。而後當View層調用loginToServer(String userName, String password)
方法成功時,通知View層顯示加載動畫並調用ILoginModel層的login(String userName, String password)
方法向服務器發送登錄請求。當登錄成功後(即Model層通知loginSucceed方法時)經過loginView.showProgress(false)
方法通知View層隱藏加載動畫,並通知View登錄成功。
1
2
3
4
5
6
7
|
loginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//模擬登錄,不須要帳號密碼
loginPresenter
.loginToServer("","");
}
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Override
public void showProgress(boolean enable) {
if (enable){
progressBar
.setVisibility(View.VISIBLE);
loginLayout
.setVisibility(View.GONE);
}else {
progressBar
.setVisibility(View.GONE);
loginLayout
.setVisibility(View.VISIBLE);
}
}
@Override
public void showLoginView() {
Toast
.makeText(LoginActivity.this,"登錄功",Toast.LENGTHSHORT).show();
finish();
}
|
上面時實現了ILoginView接口的兩個方法。
結合上面的代碼能夠看出,當點擊登錄按鈕的監聽事件時,咱們不須要關注業務邏輯,只須要調用loginPresenter.loginToServer("","");
方法便可,而後根據實際狀況實現View層中ILoginView接口的方法便可,這樣達到了UI業務與邏輯徹底分離的目的。