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 { 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() { } @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来写代码,测试起来也相对容易,但是这种方式也有弊端:容易造成接口膨胀。这也印证了任何事物都有两面性,选择适合当前项目的架构,才是王道。