Wen

Android MVP学习笔记 (上)

什么?作为一名Android工程师还没听说过MVP?它火了很久了都快要过时了。嗯,其实说的是我自己。赶紧来学习下。

MVP简介


MVP(View、Model、Presenter),类似的架构模式还有MVC和MVVM等,目的都是为了分离UI层和数据层,区别就在于对两者分离和同步的处理方式。View负责UI的绘制,Model负责数据的获取和存储等相关操作,Presenter作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。

那MVP是怎么处理的呢?这里只考虑最简单最基本的情况,学习几篇文章之后我的理解主要是两点:

  1. View不直接与Model交互,而是通过与Presenter交互来与Model间接交互
  2. Presenter与View的交互是通过接口来进行的

Talk is cheap,show me the code。先来看两个最简单的代码例子来增加对它的理解。

Weather Demo


这是一个天气查询的Demo,通过访问Web服务获取地区的天气信息(返回为JSON),然后在Activity中用TextView展示出来,效果如下图:

这个Demo出自 https://rocko.xyz/2015/02/06/Android中的MVP/ 。源代码在作者的一个大repo里面,我把它提取出来放在了这里方便大家下载运行。注意,如果运行后无法获取天气信息,不是app的问题,因为我用自己的服务器模拟了天气web服务然后让app指向了我的服务器,app可以正常工作。不过因为不影响本教程我没去深入研究原因。Demo的目录结构如下图:

在这个Demo里,ModelViewPresenter每一层都只有一个类,而且作者为每个类都建立了一个interface,对应关系如下:

MVP层 Interface Implementation
Model WeatherModel WeatherModelImpl
View WeatherView WeatherActivity
Presenter WeatherPresenter WeatherPresenterImpl

View

注意这里的View就是App中唯一的Activity:WeatherActivity。先看View的interfaceWeatherView

1
2
3
4
5
6
public interface WeatherView {
void showLoading();
void hideLoading();
void showError();
void setWeatherInfo(Weather weather);
}

View的interface里应该定义这个View支持的所有操作,因为Presenter只能通过这个interface来操作View,这也是我觉得MVP不同于MVC的两点之一。上面代码中,View支持四种操作:

  1. showLoading():显示loading对话框 (等待Web服务返回结果时显示loading)
  2. hideLoading():隐藏loading对话框 (收到Web服务返回结果时隐藏loading)
  3. showError():显示Error信息
  4. setWeatherInfo():显示天气信息 (收到Web服务返回结果时显示天气信息)

再来看View的实现类WeatherActivity

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
46
47
48
public class WeatherActivity extends BaseActivity implements WeatherView, View.OnClickListener {
...
private WeatherPresenter weatherPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
...
weatherPresenter = new WeatherPresenterImpl(this); //传入WeatherView
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_go:
weatherPresenter.getWeather(cityNOInput.getText().toString().trim());
break;
}
}
@Override
public void showLoading() {
...
}
@Override
public void hideLoading() {
...
}
@Override
public void showError() {
...
}
@Override
public void setWeatherInfo(Weather weather) {
WeatherInfo info = weather.getWeatherinfo();
city.setText(info.getCity());
...
}
}

主要看三点:

  1. View里实现了interface定义的函数 (好像是废话)
  2. View里并没有对Model (数据直接操作) 的引用
  3. View里有Presenter的引用,而且把自己也传给了Presenter (对应View和Presenter的双向联系)

View里引用Presenter的地方:

  • 初始化Presenter,过程中向Presenter传入View的引用。这里是新建一个Presenter的instance,也可以是注入或其他方式获得Presenter。
  • 通过Presenter获取数据。本例中是在点击按钮时,调用Presenter的getWeather()来获取天气数据。

View实现的Interface的函数,就是提供给Presenter来调用的,下面看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
public class WeatherPresenterImpl implements WeatherPresenter, OnWeatherListener {
/*Presenter作为中间层,持有View和Model的引用*/
private WeatherView weatherView;
private WeatherModel weatherModel;
public WeatherPresenterImpl(WeatherView weatherView) {
this.weatherView = weatherView;
weatherModel = new WeatherModelImpl();
}
@Override
public void getWeather(String cityNO) {
weatherView.showLoading();
weatherModel.loadWeather(cityNO, this);
}
@Override
public void onSuccess(Weather weather) {
weatherView.hideLoading();
weatherView.setWeatherInfo(weather);
}
@Override
public void onError() {
weatherView.hideLoading();
weatherView.showError();
}
}

同样注意三点:

  1. Presenter同时有Model和View的引用。
  2. View是在构造函数中传进来的,Model是新建了一个instance。
  3. Presenter把自己以interface的形式(OnWeatherListener)通过weatherModel.loadWeather(cityNO, this)传给了Model。

Model

1
2
3
4
5
6
7
public class WeatherModelImpl implements WeatherModel {
@Override
public void loadWeather(String cityNO, final OnWeatherListener listener) {
/*数据层操作*/
...
}
}

这里的Model只负责通过网络获取天气数据,并通过OnWeatherListener来调用Presenter。

总结


我看了还多MVP的理论文字介绍,都觉得不够清晰,还是分析代码来的实在,上面的demo也算能让我们“get hands dirty”了,但似乎有些意犹未尽的感觉。下一篇会再看一个demo,并简单做些文字总结。

转载请注明出处:http://zhaowen.io/post/Android_MVP_Learning_Notes_1/