MVP模式是MVC模式在Android上的一种变体,要了解MVP就得先了解MVC。在MVC模式中,Activity应该是属于View这一层。而实质上,它既承担了View,同时也包含一些Controller的东西在里面。这对于开发与维护来说不太友好,耦合度大高了。把Activity的View和Controller抽离出来就变成了View和Presenter,这就是MVP模式。
一、MVC模式
MVC模式的结构分为三部分,实体层的Model,视图层的View,以及控制层的Controller。
- 其中View层其实就是程序的UI界面,用于向用户展示数据以及接收用户的输入
- 而Model层就是JavaBean实体类,用于保存实例数据
- Controller控制器用于更新UI界面和数据实例
二、MVP模式
MVP模式的核心思想
MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。这就是MVP模式,现在这样的话,Activity的工作的简单了,只用来响应生命周期,其他工作都丢到Presenter中去完成。从上图可以看出,Presenter是Model和View之间的桥梁,为了让结构变得更加简单,View并不能直接对Model进行操作,这也是MVP与MVC最大的不同之处。
MVP模式的作用
- 分离了视图逻辑和业务逻辑,降低了耦合
- Activity只处理生命周期的任务,代码变得更加简洁
- 视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性
- Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试
- 把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存泄露和OOM
其中主最要的是:1,简化了Activity的代码数量,使之更加加简介清晰;2,方便进行单元测试;3,有效的避免Activity的内存泄漏。
三、简单的Login Demo
1.项目结构
2.代码
package com.zhoujp.mvpdemo.biz;
public interface IUserBiz {
public void login(String username, String password, OnLoginListener loginListener);
}
package com.zhoujp.mvpdemo.bean;
public class UserBean {
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.zhoujp.mvpdemo.biz;
import com.zhoujp.mvpdemo.bean.UserBean;
public interface OnLoginListener {
void loginSuccess(UserBean user);
void loginFailed();
}
package com.zhoujp.mvpdemo.biz;
import com.zhoujp.mvpdemo.bean.UserBean;
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 ("zhoujp".equals(username) && "123".equals(password)) {
UserBean user = new UserBean();
user.setUsername(username);
user.setPassword(password);
loginListener.loginSuccess(user);
} else {
loginListener.loginFailed();
}
}
}.start();
}
}
package com.zhoujp.mvpdemo.presenter;
import com.zhoujp.mvpdemo.bean.UserBean;
import com.zhoujp.mvpdemo.biz.IUserBiz;
import com.zhoujp.mvpdemo.biz.OnLoginListener;
import com.zhoujp.mvpdemo.biz.UserBiz;
import com.zhoujp.mvpdemo.view.IUserLoginView;
import android.os.Handler;
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 UserBean 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();
}
}
package com.zhoujp.mvpdemo.view;
import com.zhoujp.mvpdemo.bean.UserBean;
public interface IUserLoginView {
String getUserName();
String getPassword();
void clearUserName();
void clearPassword();
void showLoading();
void hideLoading();
void toMainActivity(UserBean user);
void showFailedError();
}
package com.zhoujp.mvp;
import com.example.simplelogindemo.R;
import com.zhoujp.mvpdemo.bean.UserBean;
import com.zhoujp.mvpdemo.presenter.UserLoginPresenter;
import com.zhoujp.mvpdemo.view.IUserLoginView;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
public class MainActivity extends Activity implements IUserLoginView {
private EditText edt_name, edt_pass;
private Button btn_login, btn_clear;
private ProgressBar mPbLoading;
private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
edt_name = (EditText) findViewById(R.id.edt_name);
edt_pass = (EditText) findViewById(R.id.edt_pass);
btn_login = (Button) findViewById(R.id.btn_login);
btn_clear = (Button) findViewById(R.id.btn_clear);
mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);
btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mUserLoginPresenter.login();
}
});
btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mUserLoginPresenter.clear();
}
});
}
@Override
public String getUserName() {
return edt_name.getText().toString();
}
@Override
public String getPassword() {
return edt_pass.getText().toString();
}
@Override
public void clearUserName() {
edt_name.setText("");
}
@Override
public void clearPassword() {
edt_pass.setText("");
}
@Override
public void showLoading() {
mPbLoading.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
mPbLoading.setVisibility(View.GONE);
}
@Override
public void toMainActivity(UserBean 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();
}
}