在学习MVP之前我们先了解一下MVC
MVC
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
- Model(模型):数据层,负责处理业务逻辑,监听网络或数据库接口;
- View(视图):界面(UI)层,显示来源于Model的数据;
- Controller(控制):逻辑层,传递用户的交互和更新Model的数据;
在Android中,Activity/Fragment既有View的性质,也具有Controller的性质,导致Activity/Fragment很重。MVC中View会与Model直接交互,所以Activity/Fragment与Model的耦合性很高。
优点:
- 耦合性降低,MVC本质是分层耦合,减少代码之间的相互影响。
- 可扩展性好,由于耦合性低,在增加需求时,改动小,bug出现的机率小。
缺点:
随着项目的增大,Activity/Fragment的代码会变得臃肿。
MVP
MVP是Model,View和Presenter的简称。是非常有名的MVC模式的演化版。MVP模式将MVC中的Controller变为Presenter,同时改变了通信方向。View与Model隔离,Presenter负责完成View层与Model层的交互。
- View 对应于Activity,负责View的绘制以及与用户交互
- Model 依然是业务逻辑和实体模型
- Presenter 负责完成View于Model间的交互
各部分之间的通信,都是双向的。
优点:
- Model层可以封装复用,可以极大的减少代码量。
- View和Model之间的耦合度降低,使各自更关注自身业务。
- 模型与视图完全分离,我们可以修改视图而不影响模型。
- 可以更加高效地使用模型,因为所有的交互都发生在Presenter内部。
- 我们可以将一个视图用于多个视图,而不需要改变Presenter内部的逻辑。这个特性非常有用,因为视图的变化总是比模型的变化要频繁。
- 把程序逻辑放在Presenter中,我们就可以脱离用户接口来测试这些逻辑了。(单元测试)
缺点:
随着业务逻辑的增加,一个页面可能会非常复杂,UI 的改变是非常多,造成 View 的接口会很庞大,Presenter层的代码也会越来越臃肿。
具体实例:
View接口: View接口是Activity与Presenter层的中间层,它的作用是根据具体业务的需要,为Presenter提供调用Activity中具体UI逻辑操作的方法。
public interface IMvpview {
/**
* 显示正在加载进度框
*/
void showLoading();
/**
* 隐藏正在加载进度框
*/
void hideLoading();
/**
* 当数据请求成功后,调用此接口显示数据
*
* @param data 数据源
*/
void showData(String data);
/**
* 当数据请求失败后,调用此接口提示
*
* @param msg 失败原因
*/
void showFailureMessage(String msg);
/**
* 当数据请求异常,调用此接口提示
*/
void showErrorMessage();
}
Presenter接口: presenter接口是presenter和model之间的中间层,它的作用是为Model层提供调用Presenter层的方法。
public interface IMvpPresenter {
void onSuccess(String data);
void onFailure(String msg);
void onError();
void onComplete();
}
Model接口: Model接口作用是提供具体业务逻辑处理的方法
public interface IMvpModel {
/**
* 数据请求
*
* @param data
*/
void getNetData(String data);
}
Activity类
public class MainActivity extends AppCompatActivity implements IMvpview, View.OnClickListener {
private MvpPresenter mvpPresenter;
//进度条
ProgressDialog progressDialog;
TextView text;
Button btn1, btn2, btn3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text);
btn1 = (Button) findViewById(R.id.btn1);
btn2 = (Button) findViewById(R.id.btn2);
btn3 = (Button) findViewById(R.id.btn3);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
// 初始化进度条
progressDialog = new ProgressDialog(this);
progressDialog.setCancelable(false);
progressDialog.setMessage("正在加载数据");
mvpPresenter = new MvpPresenter();
mvpPresenter.attachView(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn1:
mvpPresenter.getData("normal");
break;
case R.id.btn2:
mvpPresenter.getData("failure");
break;
case R.id.btn3:
mvpPresenter.getData("failure");
break;
}
}
@Override
public void showLoading() {
if (!progressDialog.isShowing()) {
progressDialog.show();
}
}
@Override
public void hideLoading() {
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
@Override
public void showData(String data) {
text.setText(data);
}
@Override
public void showFailureMessage(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
text.setText(msg);
}
@Override
public void showErrorMessage() {
Toast.makeText(this, "网络请求数据出现异常", Toast.LENGTH_SHORT).show();
text.setText("网络请求数据出现异常");
}
@Override
protected void onDestroy() {
super.onDestroy();
// 断开View引用
mvpPresenter.detachView();
}
}
Presenter类
public class MvpPresenter implements IMvpPresenter {
private IMvpview iMvpview;
private MvpModel mvpModel;
public void getData(String params) {
iMvpview.showLoading();
mvpModel.getNetData(params);
}
/**
* 绑定view,一般在初始化中调用该方法
*/
public void attachView(IMvpview iMvpview) {
this.iMvpview = iMvpview;
mvpModel = new MvpModel(this);
}
/**
* 断开view,一般在onDestroy中调用
*/
public void detachView() {
this.iMvpview = null;
}
/**
* 是否与View建立连接
* 每次调用业务请求的时候都要出先调用方法检查是否与View建立连接
*/
public boolean isViewAttached() {
return iMvpview != null;
}
@Override
public void onSuccess(String data) {
if (isViewAttached()) {
iMvpview.showData(data);
}
}
@Override
public void onFailure(String msg) {
if (isViewAttached())
iMvpview.showFailureMessage(msg);
}
@Override
public void onError() {
if (isViewAttached())
iMvpview.showErrorMessage();
}
@Override
public void onComplete() {
if (isViewAttached())
iMvpview.hideLoading();
}
}
Model类
public class MvpModel implements IMvpModel {
private IMvpPresenter iMvpPresenter;
public MvpModel(IMvpPresenter iMvpPresenter) {
this.iMvpPresenter = iMvpPresenter;
}
@Override
public void getNetData(final String param) {
// 利用postDelayed方法模拟网络请求数据的耗时操作
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
switch (param) {
case "normal":
iMvpPresenter.onSuccess("根据参数" + param + "的请求网络数据成功");
break;
case "failure":
iMvpPresenter.onFailure("请求失败:参数有误");
break;
case "error":
iMvpPresenter.onError();
break;
}
iMvpPresenter.onComplete();
}
}, 2000);
}
}
1、会造成接口类爆炸问题。使用MVP模式去构建项目,会造成类文件和接口文件的过多,进而增大包的体积
解决 写一个Contract接口,然后把与MVP相关接口全部列入到里面去
2、会造成内存泄漏的问题。当用户关闭了View层,但这时Model层如果仍然在进行耗时操作,因为Presenter层也持有View层的引用,所以造成垃圾回收器无法对View层进行回收,这样一来,就造成了内存泄漏。
解决 可以重写onDestroy()方法,在View销毁时强制回收掉Presenter;或是采用弱引用的方式(引用进行引用之前,都需要判断引用不为空,以防止空指针异常。)