(本文较为基础,都是一些介绍性的东西)

什么是MVP

MVP (Model-View-Presenter)是一种分层次的架构设计模式,派生自MVC模式。
其作用在于解耦,将项目化为结构化设计,达到可拓展可复用的目的。

在Android开发中,一般有两种见解。

一种将Activity作为一种View,一种将Activity作为Presenter。

前者是最直接的思路。
因为Acitivity直接关联了Window,很自然的就会将其作为View来使用。Presenter将作为Activity的成员变量对于进行Activity进行控制。

后者的思路也很简单。
因为Activity中封装了太多的操作,包括复杂的生命周期控制、Intent跳转、Fragment的Transition等等,我们有充足的理由认为Activity不仅仅是一个View。于是不如将它作为Presenter,而把View层的东西抽离出来。这与我们的最初学Android时的经验向背。但是有它的道理。

我现在学会的主要是前一种,后一种还没真正品出味儿来,觉得和前一个差不多。也可能真的差不多,我不敢说。所以这篇文章也只是主要记录前一种。

##为什么要用MVP
Android的结构是有问题的,和Web相比,显得十分耦合。这种耦合性主要体现在,显示层和逻辑层通通由一个类负责。这并不符合Java的设计原则。最常见的是Activity,新手(也包括以前我)很容易将所有东西都堆在Activity里,不管是逻辑方面的东西,还是显示方面的东西,甚至是数据库查询的异步都是如此。这样当项目大了,其实都不用很大,很容易就写出上千行的类,而且以后万一再添加新功能或者突然发现bug必须改结构的时候,绝对要跪。
MVP思想可以把显示和逻辑分开,将整个项目分为三层,从而提高可拓展性。避免上面提到的一些问题。

##怎么实现MVP
首先需要说明的是MVP是一种结构思想,所以没有标准的MVP实现方法。这意味着,我们每个人的MVP可能都有不同,同时每个人在不同的地方使用MVP思想写出的代码,也应略作调整来适应相应的要求。
所以我这里的实现,只是从代码的角度说明究竟什么才是MVP。真正学会的人完全可以也应该做出自己的调整。
MVP表现在代码上到底是什么样的形式呢?

Presenter

presenter是Model和View层的中间件。于Model,它通过持有的Model实例来获得数据,并且也做一些初步的处理。于View,它通过持有的View引用反过来操作View,同时也被View调用从而处理交互事件。

View

在Android中,View层可以是Activity, FragmentView, etc. 该类中理应包含着一个Presenter实例。该类的功能就只有两个,一个为操作UI所有的UI元素,比如显示个Toast,隐藏个ProgressBar。另一个功能是处理交互信息,比如点击按钮,这种函数唯一需要做的事就是调用Presenter来处理该事件。

Model

本层直接与数据进行交互,实现一些功能性函数,尤其是与数据有关的功能性函数,可以是网络,也可以是数据库。比如验证用户,增删查该,访问网络流等等。这些应该独立,也是便于测试。

Example

我们来看一个Activity作为例子

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class LoginActivity extends Activity implements LoginView, View.OnClickListener {

private ProgressBar progressBar;
private EditText username;
private EditText password;
private LoginPresenter presenter;

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

progressBar = (ProgressBar) findViewById(R.id.progress);
username = (EditText) findViewById(R.id.username);
password = (EditText) findViewById(R.id.password);
findViewById(R.id.button).setOnClickListener(this);

presenter = new LoginPresenterImpl(this);
}

@Override public void showProgress() {
progressBar.setVisibility(View.VISIBLE);
}

@Override public void hideProgress() {
progressBar.setVisibility(View.GONE);
}

@Override public void setUsernameError() {
username.setError(getString(R.string.username_error));
}

@Override public void setPasswordError() {
password.setError(getString(R.string.password_error));
}

@Override public void navigateToHome() {
startActivity(new Intent(this, MainActivity.class));
finish();
}

@Override public void onClick(View v) {
presenter.validateCredentials(username.getText().toString(), password.getText().toString());
}
}

作为View层的Activity做了以下工作。

  • 提供控制View的接口,我们看到这个Activity implements了LoginView,一会儿presenter控制View就是利用这个接口
  • 获取交互事件,通过调用Presenter的接口来进一步处理。
  • 初始化,尤其是Presenter,传入其本身来达到控制目的。
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 LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener {

private LoginView loginView;
private LoginInteractor loginInteractor;

public LoginPresenterImpl(LoginView loginView) {
this.loginView = loginView;
this.loginInteractor = new LoginInteractorImpl();
}

@Override public void validateCredentials(String username, String password) {
loginView.showProgress();
loginInteractor.login(username, password, this);
}

@Override public void onUsernameError() {
loginView.setUsernameError();
loginView.hideProgress();
}

@Override public void onPasswordError() {
loginView.setPasswordError();
loginView.hideProgress();
}

@Override public void onSuccess() {
loginView.navigateToHome();
}
}

具体的interface我就不写了。实现类做了三件事

  • 初始化,尤其是Interator,其实是这里的Model层
  • 功能控制函数,validateCredentials()这个函数其实是一个功能的代理,具体的实现是在Model里的
  • 显示控制函数,onError()这个函数,是在得到Model的响应后反过来操作View的接口。

Interator非常简单,这里就不详细说了。它只做一件事

  • 功能具体实现函数,同时按照Presenter的安排进行回调。进行接下来的操作。

##一些问题
MVP作为一个Android客户端来用是够了的(真的够了吗?其实还是会有一些问题),但是作为软件我认为还有一些问题。Android端完全可以利用自己的运算功能实现很多工具性的功能,这时候MVP就会显得有些不合脚。
就比如我现在写的一个图像处理程序MoFa。作为图像处理软件,功能多样化无疑非常重要,按钮恐怕早已上百,功能也相当多,同时有许多不同的操作状态或者叫操作阶段。

所以MoFa的难点对应的也分三点

  • 怎样将View层拆分,达到不同的解耦目的(我目前最多也就是用用include)
  • 功能多样,目前我通过持有每个功能特定的Controller来操作对应的功能,直接传入控件。
  • 怎样判断不同的阶段。Controller之间应该相互感知,比如进行了这个操作过程中,暂时没办法进行另一个操作。

我目前的办法是Activity持有多个Controller的办法,年少无知。打算什么时候有时间了就把它用一个统一的Controller来管理。用大Controller也可以复用小的Controller。

跑题了,我的意思是说MVP不能完全解决我的问题(我就是因为这个类太大不优雅而求助于MVP的,但是MVP表示爱莫能助很遗憾)。

我也会继续学习,如果万一有人能够看到这个问题,希望能够给我提供一些建议,万分感谢。 danxionglei@foxmail.com

##参考资料

  1. Model–view–presenter in Wikipedia
  2. MVP for Android: how to organize the presentation layer
  3. Android MVP - An Alternate Approach