如何理解MVP

761 阅读4分钟

MVP是从MVC衍生出来的

MVC(Model - View - Controller)

MVC模式的三个部分:

  • 模型(Model) 包括了所有数据模型与数据状态,同时负责数据的交互与存储;

  • 视图(View) 包括了呈现给用户的UI以及其他所有和用户的交互

  • 控制器(Controller) 包括了业务逻辑以及Model与View之间的交互,它像是Model与View之间的桥梁,通过Controller可以去操作Model并返回结果显示在View上。

各部分之间的通信方式

图片.png

v-c-m-c-v

MVP(Model - View - Presenter)

  • Model:负责网络的请求

  • Presenter:负责处理请求网络后的数据处理:加载中 成功 or 失败 取消加载

  • View:xml+Activity/Fragment 进行界面的展示

MVP的优点:

1.Model与View完全分离,修改互不影响

2.Model层可以封装复用,可以极大的减少代码量。

3.View和Model之间的耦合度降低,使各自更关注自身业务。

4.便于单元测试。

5.代码复用率提高。

6.代码框架更适用于快速迭代开发。

MVP的缺点:

MVP在实现代码简洁的同时,额外增加了大量的接口、类,不方便进行管理

为了解决这个问题,就会使用Contract (合同;契约;协议)对Model、Presenter 、View进行约束管理,以方便后期类的查找和维护。

图片.png 各部分之间的通信,都是双向的。

View 与 Model 不发生联系,都通过 Presenter 传递。

View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。

MVP 具体实现

1. 创建 Model 类,封装通过网络请求获取数据的过程,即 M 层

/**
 * model 层:从数据源(网络、数据库)获取数据
 */
public class DataModel {

    private DataApi mApi;

    public DataModel() {
        mApi = RetrofitHelpter.createApi(DataApi.class);
    }

    public void getData(String appKey, Callback<BaseResponse> callback) {
        Call<BaseResponse> responseCall = mApi.getData(appKey);
        // 发起请求
        responseCall.enqueue(callback);
    }

}

2. 创建一个接口,取名 DataView,负责向 V 层回调数据

public interface DataView {

    void getDataSuccess(List<ArticleBean> articleList);

    void getDataFail(String failMsg);

}

3.创建 Presenter,取名 DataPresenter

/**
 * 负责 View 层和 Model 层之间的通信,并对从 Model 层获取的数据进行处理
 */
public class DataPresenter {

    private DataView mView;
    private DataModel mModel;

    public DataPresenter(DataView dataView) {
        this.mView = dataView;
        this.mModel = new DataModel();
    }

    /**
     * 定义 View 层需要进行的 action
     */
    public void getData(String appKey) {
        mModel.getData(appKey, new Callback<BaseResponse>() {
            @Override
            public void onResponse(Call<BaseResponse> call, Response<BaseResponse> response) {
                mView.getDataSuccess(response.body().getResult().getList());
            }

            @Override
            public void onFailure(Call<BaseResponse> call, Throwable t) {
                mView.getDataFail(t.getMessage());
            }
        });
    }

}

4. 在 View 层,实现 DataView 接口,并重写方法,接收回调数据

/**
 * View 层,负责 UI 绘制以及与用户的交互
 */
public class MVPDemoAty extends AppCompatActivity implements DataView {

    private static final String APP_KEY = "dbb6893ab0913b02724696504181fe39";

    private Button btnGet;
    private RecyclerView recyclerView;

    private DataPresenter mPresenter;

    private DataAdapter mAdapter;

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

        btnGet = findViewById(R.id.btnGet);
        recyclerView = findViewById(R.id.recyclerView);

        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        mPresenter = new DataPresenter(this);

        mAdapter = new DataAdapter(this, new ArrayList<ArticleBean>());
        recyclerView.setAdapter(mAdapter);

        btnGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.getData(APP_KEY);
            }
        });

    }

    @Override
    public void getDataSuccess(List<ArticleBean> articleList) {
        mAdapter.setNewData(articleList);
    }

    @Override
    public void getDataFail(String failMsg) {
        Toast.makeText(this, failMsg, Toast.LENGTH_SHORT).show();
    }
}

注意事项

1.View只负责简单的视图更新、界面跳转的代码,只负责初始化自身,根据需要,给Presenter指派任务,然后进入“瞌睡”状态

因为相比起来,如果是Presenter太臃肿,可以根据功能不同,轻易拆分成两个甚至更多的Presenter。但是如果Activity/Fragment(View)太臃肿的话,可能就不好拆分了。

2.View和Presenter的交互,是用接口来实现的。

Activity/Fragment实现XxxContract.View接口,Presenter实现XxxContract .Presenter接口。

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

因为Presenter的生命周期,通常与Activity/Fragment是不相同的。所以Presenter在执行异步操作后,在结束的时候,都要判断View是否还处于正确的状态,在执行同步操作时不需要进行这种判断。

4.Model,作为数据源,要确保它的可复用性。

Model不应该持有Presenter的引用。

5.如果要在Presenter和Model里面使用Context,应该使用Application的Context。

尽量隔离、减少使用Android类的Presenter、Model层,会更便于我们写相关单元测试,Application的Context足够应付Presenter和Model里面的需求了。

微信图片_20210806112611.jpg

6.Presenter、Model的构造方法,要使用依赖注入

VersionModel versionModel = new 
VersionModel(getApplicationContext());
mPresenter = new SplashPresenter(versionModel);

7.关于Presenter、Model的划分

Model是“数据处理”,Presenter是“根据业务逻辑,对Model获得的结果进一步处理”。Presenter、Model同样都会进行数据处理

其实MVP三层的关系可以说是层层递进的关系

微信图片_20210806112602.jpg