AndroidMVP开发模式-Contract⽤法
说起Android⽐较流⾏的架构模型,MVC、MVP、MVVM这⼏种是最常见的,也是当前主流的架构模型,本篇通过对MVC到MVP的进化过程,给出⼀种MVP个⼈认为⽐较适合的开发模式 - Contract⽤法:
MVP前世
MVC模式(Model、View、Controller)已经开始渐渐地退出,它最⼤的痛点在于Controller中既承载了View⼜包括了业务逻辑,导致Controller最终会很庞⼤,不利于维护和可读,⽐如:
publicclassXXXActivityextentsActivity{
publicvoidonCreate(Bundle saveInstanceState){
setContentView(layout);
initView();
initData();
}
publicvoidinitView(){
view1 = findViewById(id);
view2 = findViewById(id);
...
view1.setOnClickListener( this);
view2.setXXListener(..);
...
}
publicvoidinitData(){
model.loadData(response -> {
view1.setXXX(response.XX);
view2.setXXX(response.XX);
});
...
}
@Override
publicvoidonClick(View v){
modelmit();
model.doSth();
<
}
}
即使你拆分出许多XXXHelper或者说XXXViewBlock,并且只在Controller中处理业务流程(Helper或者Block中持有Activity引⽤,不持有Model,为了不与Model耦合在⼀起),⽐如:publicclassXXXActivityextentsActivity{
privateXXViewHelper helper1;
privateXXViewHelper helper2;
publicvoidonCreate(Bundle saveInstanceState){
setContentView(layout);
initView();
initData();
}
publicvoidinitView(){
helper1 = newXXViewHelper( this); // 传⼊Activity
helper2 = newXXViewHelper( this); // 传⼊Activity
}
publicvoidinitData(){
model.loadData(response -> {
helper1.updateView(response);
helper2.updateView(response);
});
...
...
}
// 供Helper调⽤
publicvoidcommit(){
modelmit();
}
// 供Helper调⽤
publicvoiddoSth(){
model.doSth();
}
}
但仍然解决不了的问题是,Activity向这些Helper或者Block暴露的信息太多了,⽐如⽣命周期这些⽅法,
以及不希望Helper它们看到的⽅法,不利于开发者对业务流程的理解,并且看到没有?现在的Activity已经有点像Presenter了,只是⾥边还存在⼀些 Helper的初始化。
MVP今⽣
由于上述分析,为了进⼀步减轻Activity的压⼒,所以决定将Activity只做⼀些与View相关的事情,那处理业务流程的部分去哪了?这个就是新产⽣的⼀个模块-Presenter。虽然很多⽂章都有写,但我还是再次声明下它们的⼯作职责,如下:
MVP⽰意图
View: 只处理UI及页⾯效果的细节,向Presenter暴露更新UI的⽅法;并且持有Presenter的引⽤,通过Presenter对其暴露的⽅法进⾏⼀些初始化页⾯以及业务提交等动作,但不关注动作的具体实现。
Presenter:只关注业务逻辑的细节,持有View的引⽤,通过调⽤View层向其暴露的⽅法去更新UI (这⾥的View引⽤不是具体某个控件的引⽤,我们也不能让Presenter持有某⼀控件的引⽤);并且也持有⼀个或者多个model的引⽤(在于你想将Presenter,也就是业务逻辑拆分的程度,避免Presenter也像MVC中Controller⼀样被撑爆),可以使⽤model,通过对数据库或者⽹络的访问从⽽拿到数据,调⽤View暴露的⽅法去刷新UI。
Model:向Presenter暴露获取、存储、提交数据等⽅法,具体实现细节Presenter不关注;Model通过Callback 将数据返回给Presenter。
Ok,按照之前规定的原则:
•View 向 Presenter 暴露更新UI的⽅法,于是我们有了 IView
•Presenter 向 View 暴露执⾏⼀些特定业务⽅法,⽐如初始化页⾯,提交等。
于是,就产⽣了第⼀种MVP模式:
MVP第⼀种模式
MVP第⼀种模式
看到以上类图,可能会有⼈有疑问:
1.在IView中为什么我不直接写 updateView(Response r),这样不是写的接⼝⽅法更少吗?这个问题问的好!因为我
曾经也有过这样的问题,试想⼀下,如果我们把Model中的Response直接存在于Activity(View层)中,那
当我们更改Response的时候,会导致View层也需要进⾏相应的变动,还能做到View和Model的完全解耦吗?所以View 向Presenter暴露的⽅法参数⼀定要符合两点:(1). 基本数据类型(2). 公共数据类型
2.为什么要写IPresenter,让Activity直接引⽤ XXXPresenter不就好了?确实,这样做从功能上来说也可以,但是,为
了更严格⼀些,让Presenter向View暴露那些View关注的⽅法,这样开发View的同学就会⼀下⼦明⽩⾃⼰只需要关注哪些⽅法就够了;同理,Presenter持有IView的引⽤⽽不是Activity的也是这个原因。
按照以上模式,我们⽤伪代码来实现⼀下第⼀种MVP模式(以下代码不严谨,只表达意思):
Presenter:interfaceIPresenter{
voidinitData();
voidcommit();
}
classXXXPresenterimplementsIPresenter{
privateIView mView;
privateModel mModel;
privateCallback mCallback = newCallback() {
publicvoidonSuccess(Response r){
mView.cancelLoading();
if(r.url == 'initDataUrl') {
mView.updateHeader(r.header);
mView.t);
} elseif(r.url == 'commitUrl') {
// 页⾯跳转或者其他什么的
}
}
publicvoid(Error e){
mView.showError();
}
};
XXXPresenter(IView view) {
mView = view;
mModel = newModel();
}
@Override
publicvoidinitData(){
mView.showLoading();
}
@Override
publicvoidcommit(){
mView.showLoading();
}
}
View:interfaceIView{
voidupdateHeader(String header);
voidupdateContent(String content);
voidshowLoading();
voidcancelLoading();
voidshowError(String error);
} publicclassXXXActivityextendsBaseActivityimplementsIView{ privateIPresenter mPresenter;
privateTextView tvHeader;
privateTextView tvContent;
publicvoidonCreate(Bundle saveInstanceState){