MVP架构

1,444 阅读5分钟

在学习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的耦合性很高。

image.png

优点:

  1. 耦合性降低,MVC本质是分层耦合,减少代码之间的相互影响。
  2. 可扩展性好,由于耦合性低,在增加需求时,改动小,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间的交互

各部分之间的通信,都是双向的。

image.png

优点:

  1. Model层可以封装复用,可以极大的减少代码量。
  2. View和Model之间的耦合度降低,使各自更关注自身业务。
  3. 模型与视图完全分离,我们可以修改视图而不影响模型。
  4. 可以更加高效地使用模型,因为所有的交互都发生在Presenter内部。
  5. 我们可以将一个视图用于多个视图,而不需要改变Presenter内部的逻辑。这个特性非常有用,因为视图的变化总是比模型的变化要频繁。
  6. 把程序逻辑放在Presenter中,我们就可以脱离用户接口来测试这些逻辑了。(单元测试)

缺点:

随着业务逻辑的增加,一个页面可能会非常复杂,UI 的改变是非常多,造成 View 的接口会很庞大,Presenter层的代码也会越来越臃肿。

具体实例:

image.png

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;或是采用弱引用的方式(引用进行引用之前,都需要判断引用不为空,以防止空指针异常。)