一、 定义
MVP从更早的MVC框架演变过来,与MVC有一定的相似性:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。
已经有了MVC,为什么还要提出MVP?
在Android开发中,Activity并不是一个标准MVC模式中的Controller。按理说,Controller只需要接受用户的输入,然后与Model进行交互进行数据的更新。
这也正是问题所在。看过不少2000行+的Activity,真的想吐,这其中Activity担负着用户每个操作交互,庞大可以理解。但实际上Activity还同时处理着业务逻辑功能等,这是Controller不应该去做的事情,结果带来的是臃肿的Activity,可维护性低。
为了解决这个问题,引入了MVP框架。
二、 好处
1.View与Model并不直接进行交互,而是通过Presenter连接彼此。View中不存在Model,从而也不会存在业务逻辑;
2.Presenter与View的交互是通过接口来实现的,耦合度低,也有利于单元测试;
3.Presenter是基于行为的,一个Presenter可用于多个View,增强了代码复用。
三、 简单例子
我们现在来模拟一个界面,用户修改用户名。
先看看码完的demo包结构:
接下来,我们慢慢来解析MVP每一元素。
(一)、Model
往业务上看,User这个实体类必须得有,还必须要有相关的业务方法,更新userName。
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;
}
}
public interface IUserNameUpdateBiz {
public void updateUserName(String username, OnUserNameUpdateListener listener);
}
public class UserNameUpdateBiz implements IUserNameUpdateBiz {
@Override
public void updateUserName(final String username, final OnUserNameUpdateListener listener) {
// 模拟访问服务修改username
new Thread() {
@Override
public void run() {
try {
// 模拟耗时操作
sleep(1200);
} catch (InterruptedException e) {
e.printStackTrace();
}
int resultCode = 1; // 这里假设访问网络修改成功
if (resultCode == 1) {
User user = new User();
user.setUserName("cxy");
listener.updateSuccess(username);
} else {
listener.updateError();
}
}
}.start();
}
}
这个业务很简单,在这里抽象出一个业务方法-updateUserName,并且付诸实现,这里做的是耗时操作,访问网络去修改userName。模拟了耗时操作,并使用了一个接口做网络返回状态的监听。
(二)、View
View与Model完全独立,在View中不会看到Model的身影,它们是通过Presenter进行交互的。而View与Presenter的交互方式是通过接口实现。在Android中,View的体现一般有Activity、Fragment、View等等。在UI层启动之后,实例化相应的Presenter。接下来先看下View与Presenter之间的接口IUserNameUpdateView:
public interface IUserNameUpdateView {
public void showLoading();
public void hideLoading();
public String getUserName();
public void clearInput();
public void showFailedError();
}
定义了这么多的方法,这都是要Activity具体去实现的方法,实现各式各样的交互,当然这些方法不包括任何逻辑。
终于迎来了View了,在MVP中,Activity就是View。
public class UserNameUpdateActivity extends Activity implements IUserNameUpdateView {
private EditText mETUserName;
private Button mBtnSure;
private ProgressBar mPb;
private UserNameUpdatePresenter mUserNameUpdatePresenter = new UserNameUpdatePresenter(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_username_update);
mETUserName = (EditText) findViewById(R.id.et_mobile);
mBtnSure = (Button) findViewById(R.id.btn);
mPb = (ProgressBar) findViewById(R.id.pb);
mBtnSure.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mUserNameUpdatePresenter.updateUserName();
}
});
}
@Override
public void showLoading() {
mPb.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
mPb.setVisibility(View.GONE);
}
@Override
public String getUserName() {
return mETUserName.getText().toString();
}
@Override
public void clearInput() {
mETUserName.setText("");
}
@Override
public void showFailedError() {
}
}
这里接口会引导Activity去做事,我们关键要知道的是接口的方法要如何定义,这里要根据具体的业务来了。
(三)、Presenter
既使View与Model完全隔离,又连接彼此,“又爱又恨”吶。
public class UserNameUpdatePresenter {
private IUserNameUpdateBiz userNameUpdateBiz;
private IUserNameUpdateView userNameUpdateView;
public UserNameUpdatePresenter(IUserNameUpdateView userNameUpdateView) {
this.userNameUpdateView = userNameUpdateView;
this.userNameUpdateBiz = new UserNameUpdateBiz();
}
public void updateUserName() {
userNameUpdateView.showLoading();
userNameUpdateBiz.updateUserName(userNameUpdateView.getUserName(), new OnUserNameUpdateListener() {
@Override
public void updateSuccess(String userName) {
// 存入本地user信息
// UserConfig.updateNativeUserName(userName);
}
@Override
public void updateError() {
userNameUpdateView.showFailedError();
}
});
}
}
既然连接Model与View,又实现抽象,所以有这二者的接口对象,在相关的业务方法中,具体是让Model去执行,View做对应的显示。