0%

谈谈MVP

1. 前言

MVP由MVC演变而来,Android的项目架构也是基于MVC的,V层主要由xml来承载,C层主要是Fragment/Activity,M层主要是自己写的业务逻辑,这样划分没啥问题。可以理想很美好,现实很残酷,到后面你会发现很多的Fragment/Activity即做了一些V层的工作(比如控制View的状态),又做了一些C层工作(操作Model、用户数据输入等),又做了一些M层工作(读写数据库,网络请求等),最终导致Fragment/Activity越来越膨胀,上千行甚至几千行的Fragment/Activity也屡见不鲜。

MVC中,V可以直接访问M,导致V和M的关联性比较强,往往导致当V改变时,M也得跟着变动。而在移动客户端中,V变动很频繁(比如单列表改成双列表或者瀑布流),这样M又得跟着变动。那有没有另外一种比较好的方式呢?MVP应运而生。

MVP主要思路是在M和V之间加一层表现层Presnter,用来隔绝M和V,由P来代替原先的Cotroller。这样,不管是M层或V层有变化时,只需要少量修改甚至是不修改Presnter(P层的接口是定好的)就可以达到目的。下面通过一个小例子说明MVP如何操作。

2. 如何操作

2.1 定义接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface PluginContract {
interface View extends BaseView {
void updateView();
}
interface Presenter extends BasePresenter {
void doSomething(Param param);
}
class Param {
String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
}
public interface BasePresenter {
void start();
void end();
}
public interface BaseView {
}

2.2 具体实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class PluginPresenter implements PluginContract.Presenter {
// 如果有具体业务逻辑Model的话,Presenter也应该持有一个Model类引用
private PluginContract.View mView;
private PluginContract.Param mParam;
public PluginPresenter(PluginContract.View view) {
mView = view;
}
public PluginPresenter(PluginContract.View view, @NonNull PluginContract.Param param) {
mView = view;
mParam = param;
}
@Override
public void start() {
// 初始化操作
}
@Override
public void end() {
// 释放资源
}
public void doSomething() {
//其中可能会用到View的方法,如
// mView.updateView();
}
@Override
public void doSomething(PluginContract.Param param) {
// 做具体业务逻辑
}

}

一般使用Fragment实现PluginContract.View接口,在fragment.onCreate()方法进行初始化,收到某些消息的时候去做某事,比如点击去请求网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class PCFragment extends BaseFragment implements PluginContract.View, View.OnClickListener {
private PluginContract.Presenter mFavPresenter;
private String mUrl;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Bundle args = getArguments();
if (args != null) {
mUrl = args.getString(PARAM_URL);
}

PluginContract.Param param = new PluginContract.Param();
param.setUrl(mUrl);
mPresenter = new PluginPresenter(this, param);
mPresenter.start();
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.do_something_layout:
if (mPresenter != null) {
mPresenter.doSomething();
}
break;
}
}
}

3. 总结

目前,新闻的很多地方如分享,弹幕,收藏,直播,都采用了这种设计方式,代码看起来清晰了很多。这种组织代码方式的好处是解放了Fragment/Activity,不用什么东西都往Fragment/Activity堆,代码灵活性比较高,而且复用性比较好,一个PluginPresenter写好之后,其他地方也能复用。一些比较通用的组件如分享写成一个SharePresenter之后基本上不用做啥改动就可以复用到其他地方。而且这种方式写测试代码也比较容易。当然,如果能完全按照MVC来写代码,测试起来也相对容易,但是这种方式也有弊端:容易造成接口膨胀。这也印证了任何事物都有两面性,选择适合当前项目的架构,才是王道。