MVP模式已經流行很長一段時間了,以前只是聽到關於MVP的一些東西,譬如:MVP是MVC的進化版,MVP模式讓View層和Model層完全解耦了。
這裏先簡單回顧一下Android中MVC模式:M即是代表業務邏輯和實體模型;V即是代碼對應的View,也就是界面;C即是代表控制層,對應着Activity或者Fragment;但是很多時候數據處理都在Activity或者Fragment中,View可以直接訪問Model,這樣就不可避免一些業務邏輯了,而且有許多業務邏輯都是在View層處理的,所以導致很多代碼也就無法重用了。
MVP模式與MVC模式最大的區別就是View不與Model打交道,而是通過Presenter來與Model交互,而且Presenter實現的邏輯代碼與具體View是沒有關聯的,它不屬於任何具體的View,而只是通過約定好的接口來交互。
根據一個簡單的例子來對MVP模式進行更深刻的理解,圖1就是要實現的UI效果,一個簡單的登陸界面:
圖2是項目文件結構圖:
圖3是MVP示意圖和項目中各類的對應關係,以便更好地理解:
解析:
- UserBean是實體類,這個沒什麼可以講的,直接上代碼:
public class UserBean {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
- Model部分是負責數據邏輯處理的部分,這個例子比較簡單,就只有登陸這個功能而已,方便我們以後做業務拓展,我們先寫一個UserModel 的接口UserModelInterface,代碼如下:
public interface UserModelInterface {
void login(String name,String password,LoginListener loginListener);
}
UserModel 實現代碼如下:
public class UserModel implements UserModelInterface {
@Override
public void login(String name, String password, LoginListener loginListener) {
//判斷用戶名是否爲空
if (TextUtils.isEmpty(name)){
loginListener.nameIsEmpty();
return;
}
//判斷密碼是否爲空
if (TextUtils.isEmpty(password)){
loginListener.passwordIsEmpty();
return;
}
//用戶名和密碼都不爲空的情況下執行登陸操作,這裏就實現登陸的網絡請求代碼了,直接默認登陸成功,如果登陸失敗則回調loginListener.loginFailed();
loginListener.loginSuccess();
}
}
LoginListener 這個接口主要是用於Model和Presenter的交互,把Model的業務邏輯處理接口返回出去給Presenter,然後Presenter再通過接口(LoginView)把結果返回給View層,這大概就是MVP的模式的核心了,先不急,先上LoginListener的代碼:
public interface LoginListener {
void loginSuccess();
void loginFailed();
void nameIsEmpty();
void passwordIsEmpty();
}
結合這個案例來講,無非就兩種結果,登陸成功或者失敗,然後執行登陸之前要判斷用戶名和密碼是否爲空,所以就有以上四個方法了。
- 接下來看我們用於View(MVPLoginActivity)和Presenter(LoginPresenter)交互的接口–LoginView,這個接口主要是從分析界面來得到的業務邏輯,譬如:用戶有在登陸成功後我們需要進行頁面的跳轉,登陸失敗後的我們需要彈出一個Toast來提示用戶,或者更改界面提示等等。所以就有以下的代碼實現:
public interface LoginView {
String getName();
String getPassword();
void setName(String name);
void setPassword(String password);
void loginSuccess();//登錄成功
void loginFailed();//登錄失敗
void nameIsEmpty();//名字爲空
void passwordIsEmpty();//密碼爲空
}
- 既然說LoginView是用於View和Presenter的交互的,現在問題來了,LoginView是怎麼來連接MVPLoginActivity和LoginPresenter的呢?其實很簡單,只需要MVPLoginActivity去實現LoginView這個接口,直接上MVPLoginActivity代碼:
public class MVPLoginActivity extends AppCompatActivity implements LoginView{
private AutoCompleteTextView email;
private EditText passwordEt;
private Button email_sign_in_button;
private LoginPresenter loginPresenter=new LoginPresenter(this);//實例化Presenter
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
email= (AutoCompleteTextView) findViewById(R.id.email);
passwordEt= (EditText) findViewById(R.id.password);
email_sign_in_button= (Button) findViewById(R.id.email_sign_in_button);
email_sign_in_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loginPresenter.login();//登錄登錄登錄登錄登錄
}
});
}
@Override
public String getName() {
return email.getText().toString();
}
@Override
public String getPassword() {
return passwordEt.getText().toString();
}
@Override
public void setName(String name) {
email.setText(name);
}
@Override
public void setPassword(String password) {
passwordEt.setText(password);
}
@Override
public void loginSuccess() {
Log.i("ysc","loginSuccess");
}
@Override
public void loginFailed() {
Log.i("ysc","loginFailed");
}
@Override
public void nameIsEmpty() {
Log.i("ysc","nameIsEmpty");
}
@Override
public void passwordIsEmpty() {
Log.i("ysc","passwordIsEmpty");
}
}
在這個地方就出現了我們全面說了很多的用於View和Model交互的P(LoginPresenter)這個類了,在界面(MVPLoginActivity )實例化時也實例化LoginPresenter,在點擊按鈕時執行loginPresenter.login(),然後View就可以不用關係其他情況了,然後在下面看LoginPresenter的代碼:
public class LoginPresenter {
private LoginView loginView;
private UserModel userModel;
public LoginPresenter(LoginView loginView) {
this.loginView = loginView;
this.userModel=new UserModel();
}
public void login(){
userModel.login(loginView.getName(), loginView.getPassword(), new LoginListener() {
@Override
public void loginSuccess() {
loginView.loginSuccess();
}
@Override
public void loginFailed() {
loginView.loginFailed();
}
@Override
public void nameIsEmpty() {
loginView.nameIsEmpty();
}
@Override
public void passwordIsEmpty() {
loginView.passwordIsEmpty();
}
});
}
}
先看構造方法
public LoginPresenter(LoginView loginView) {
this.loginView = loginView;
this.userModel=new UserModel();
}
先把Loginview傳進來用戶與view層的交互,再實例化Model(UserModel),前面有說過業務處理都在Model裏,也就是userModel.login()這個方法,然後Model的處理結構通過接口LoginListener來告知LoginPreseter,LoginPresenter再通過Loginview來更新界面,這樣就完成View與Model的完全解耦了。
- LoginView:就是寫一些界面需要業務邏輯,然後可以實現相對應的UI操作。
- MVPLoginActivity:單純的界面,實現LoginView接口,並且實例化Presenter。
- Presenter:LoginPreseter作爲MVPLoginActivity和UserModel的中間層,把通過LoginView拿到的界面數據傳給UserModel處理,UserModel處理完的結果返回回來之後,通過LoginView來更新界面或者做相應的UI操作。
- MVPLoginActivity和LoginPreseter,LoginPreseter和UserModel之間的交流都是通過接口來處理,前者通過LoginView,後者通過LoginListener。
- 從以上分析看來MVP模式確實完成了View層和Model的完全解耦
以上就是個人看過很多篇關於MVP方面的博客資料的後,動手寫的demo,順便也寫成博客,歡迎多多指正。