MVP基础实例

401 阅读5分钟

简单介绍MVc/MVp

  1. MVC全名是(ModelViewController)M-Model-模型、V-View-视图、C-Controller-控制器,MVP作为MVC的演化版本,

  2. MVP所对应的意义:M-Model-模型、V-View-视图、P-Presenter-表示器。

  3. 其中M层处理数据,业务逻辑等。V层处理界面显示结果;C层起到桥梁作用,来控制V层和M层的通信,来进行分离视图显示和业务逻辑层。

  • 视图层(View) 一般采用XML文件进行描述,XML理解为AndroidApp的View,使用的时候可以很方便的引入;同时也方便后期界面修改,逻辑中与界面对应的id,如没有变化则代码不需要修改,很好的争强了代码的可维护性。
  • 控制层(Controller/Presenter) 在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。
  • 模型层 针对业务模型,建立数据结构和相关类,理解为AndroidApp的Model,Model是与View没有关系,而与业务相关的。对数据库的操作、对网络等的操作都应该在Model里处理,业务等计算操作也必须放在这层,就是应用程序中的二进制的数据。

MVC与MVP对比图如下:

image.png

  • 什么是MVP:

View:负责绘制UI元素、与用户进行交互(在Android中体现为Activity);为Presenter提供调用Activity中具体UI逻辑操作的方法。

Model:负责储存、检索操作数据。

Presenter:作为View和Model交互的中间纽带,处理与用户交互的负责逻辑,它的作用是为Model层提供调用Presenter层的方法。

1.View只负责简单的视图更新、界面跳转的代码。

image.png

个人觉得View从一开始就把自己本身的任务安排好了,再进行给Presenter所需的任务进行分配,如果把View和Presenter进行分开。如果是Presenter太臃肿的话在不同的情况下会现的不仅要多敲一个Fragment,而且在Activity中也是做反复性的工作:new Fragment,addFragment, new Present……

所以View只是做了初始化的动作,根据Presenter所需要的任务分配进行,当指令分配好了,在进行“偷懒”。

2.但是Presenter真的有必要实现接口吗?

在MVP模式中,Presenter是高层次模块,而View是低层次模块。 而使用依赖倒置原则主要是为了高层和低层模块的解耦,方便替换低层次模块的时候不会影响到高层模块,所以在MVP模式中,View需要实现接口,你可以随时将View替换成Activity,Fragment或者其他,并不会印象到Presesnter。

3.Presenter异步任务回来后,通知View更新UI之前,要先判断Activity是否为null,或者Fragment是否已经从activity中移除。

Presenter的生命周期,因为怕 Activity 被回收,然后又调用 View 的方法,出现空指针异常问题,就能有效得避免了在异步任务完成时,Activity/Fragment却已经被销毁而导致的空指针等问题。同时不需要种判断。

4.Context。如果你要在Presenter和Model里面使用Context,那么你应该使用Application的Context,而不是Activity的Context。

理解:Context 使用时注意可以用 ApplicationContext 就用 ApplicationContext, 不能用 ApplicationContext 才用 Context。

实例代码如下:

View接口:

View接口是Activity与Presenter层的中间层,它的作用是根据具体业务的需要,为Presenter提供调用Activity中具体UI逻辑操作的方法

package cn.study.mvp.View;

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接口作用是提供具体业务逻辑处理的方法

package cn.study.mvp.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;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button1 = (Button) findViewById(R.id.btn_1);
        button1.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.btn_1:
                mvpPresenter.getData("normal");
                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;
    }

    /**
     * 获取网络接口数据
     *
     * @param param 请求参数
     */
    @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);
    }
}

总结

通过对todo-mvp分析,再次了解学习了MVP。从google提供的例子中可以看出,MVP的实现较为简单,model、view和presenter各个职责明确,便于扩展维护。contract契约类的出现,使得model和presenter结构更加清晰明了。Activity和Fragment的配合,使得Activity职能更为简化,同时View的实现更加灵活。