MVP是从MVC衍生出来的
MVC(Model - View - Controller)
MVC模式的三个部分:
-
模型(Model) 包括了所有数据模型与数据状态,同时负责数据的交互与存储;
-
视图(View) 包括了呈现给用户的UI以及其他所有和用户的交互;
-
控制器(Controller) 包括了业务逻辑以及Model与View之间的交互,它像是Model与View之间的桥梁,通过Controller可以去操作Model并返回结果显示在View上。
各部分之间的通信方式
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进行约束管理,以方便后期类的查找和维护。
各部分之间的通信,都是双向的。
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里面的需求了。
6.Presenter、Model的构造方法,要使用依赖注入
VersionModel versionModel = new
VersionModel(getApplicationContext());
mPresenter = new SplashPresenter(versionModel);
7.关于Presenter、Model的划分
Model是“数据处理”,Presenter是“根据业务逻辑,对Model获得的结果进一步处理”。Presenter、Model同样都会进行数据处理
其实MVP三层的关系可以说是层层递进的关系