[关闭]
@linux1s1s 2016-04-26T11:39:41.000000Z 字数 4707 阅读 2152

MVX Android设计架构浅析-MVP

Android_Architecture 2016-04


简介

MVX Android设计架构浅析-MVC 读过这篇文章以后,应该对MVC框架有个大概的了解,这也是大部分Android应该的常用框架,但是这种框架给人的感觉更像是 View-Module框架,因为View层的确没有显现出来,而且View和Module的耦合度较高,到最后会像下图一样,业务逻辑和View展现紧密耦合在一起,相互交错,随着项目的壮大以后很难维护。

此处输入图片的描述

这么复杂的交互基本集中在Activity或者Fragment中,这样Controller每一部分都不能重复利用,无法轻易的测试、或者调试和重构。

如果使用MVP,那会是什么画面?
此处输入图片的描述

复杂的任务被分成细小的任务,并且很容易解决。越小的东西,bug越少,越容易debug,更好测试。在MVP模式下的View层将会变得简单,所以即便是他请求数据的时候也不需要回调函数。View逻辑变成十分直接。

简化的交互图

经过上面的表述,我们体验到MVP的优势,所以这样有必要再温习一下MVP的交互图
此处输入图片的描述

代码

Talk is cheap,show me the code.(废话少说,直接上代码)

读过MVX Android设计架构浅析-MVC这篇文章的同学都应该还记得,在层与层之间衔接最好是通过接口,所以我们不妨直接先定义接口

接口

View层(不仅仅局限于Android中的View,还有其他与用户交互的构件)

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

这个接口也清晰的说明了View层可以操作的方法和具有的功能

Module层(抽象数据层)

  1. public interface WeatherModel {
  2. void loadWeather(String cityNO, OnWeatherListener listener);
  3. }

这个接口比较简单,就是单一的获取Remote/Local数据

Presenter层(主要的业务逻辑层)

  1. public interface WeatherPresenter {
  2. void getWeather(String cityNO);
  3. }

这个接口主要是处理业务逻辑,完成中间的View和Module的桥接。

实现

View层(不仅仅局限于Android中的View,还有其他与用户交互的构件)

  1. public class WeatherActivity extends BaseActivity implements WeatherView, View.OnClickListener {
  2. private Dialog loadingDialog;
  3. private EditText cityNOInput;
  4. private TextView city;
  5. private TextView cityNO;
  6. private TextView temp;
  7. private TextView wd;
  8. private TextView ws;
  9. private TextView sd;
  10. private TextView wse;
  11. private TextView time;
  12. private TextView njd;
  13. private WeatherPresenter weatherPresenter;
  14. @Override
  15. protected void onCreate(Bundle savedInstanceState) {
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.activity_main);
  18. init();
  19. }
  20. private void init() {
  21. cityNOInput = findView(R.id.et_city_no);
  22. city = findView(R.id.tv_city);
  23. cityNO = findView(R.id.tv_city_no);
  24. temp = findView(R.id.tv_temp);
  25. wd = findView(R.id.tv_WD);
  26. ws = findView(R.id.tv_WS);
  27. sd = findView(R.id.tv_SD);
  28. wse = findView(R.id.tv_WSE);
  29. time = findView(R.id.tv_time);
  30. njd = findView(R.id.tv_njd);
  31. findView(R.id.btn_go).setOnClickListener(this);
  32. weatherPresenter = new WeatherPresenterImpl(this); //传入WeatherView
  33. loadingDialog = new ProgressDialog(this);
  34. loadingDialog.setTitle("加载天气中...");
  35. }
  36. @Override
  37. public void onClick(View v) {
  38. switch (v.getId()) {
  39. case R.id.btn_go:
  40. weatherPresenter.getWeather(cityNOInput.getText().toString().trim());
  41. break;
  42. }
  43. }
  44. @Override
  45. public void showLoading() {
  46. loadingDialog.show();
  47. }
  48. @Override
  49. public void hideLoading() {
  50. loadingDialog.dismiss();
  51. }
  52. @Override
  53. public void showError() {
  54. //Do something
  55. Toast.makeText(getApplicationContext(), "error", Toast.LENGTH_SHORT).show();
  56. }
  57. @Override
  58. public void setWeatherInfo(Weather weather) {
  59. WeatherInfo info = weather.getWeatherinfo();
  60. city.setText(info.getCity());
  61. cityNO.setText(info.getCityid());
  62. temp.setText(info.getTemp());
  63. wd.setText(info.getWD());
  64. ws.setText(info.getWS());
  65. sd.setText(info.getSD());
  66. wse.setText(info.getWS());
  67. time.setText(info.getTemp());
  68. njd.setText(info.getNjd());
  69. }
  70. }

View成的实现类,里面没有设计任何业务逻辑,L14 可以看出View持有WeatherPresenter的实例。而L38weatherPresenter = new WeatherPresenterImpl(this)又可以看出WeatherPresenter同样会持有View的引用。所以ViewPresenter之间是紧耦合。不过这里都是通过接口来减轻这种耦合度,并且有利于以后的代码维护。

Module层(抽象数据层)

  1. public class WeatherModelImpl implements WeatherModel {
  2. @Override
  3. public void loadWeather(String cityNO, final OnWeatherListener listener) {
  4. /*数据层操作*/
  5. VolleyRequest.newInstance().newGsonRequest("http://www.weather.com.cn/data/sk/" + cityNO + ".html",
  6. Weather.class, new Response.Listener<Weather>() {
  7. @Override
  8. public void onResponse(Weather weather) {
  9. if (weather != null) {
  10. listener.onSuccess(weather);
  11. } else {
  12. listener.onError();
  13. }
  14. }
  15. }, new Response.ErrorListener() {
  16. @Override
  17. public void onErrorResponse(VolleyError error) {
  18. listener.onError();
  19. }
  20. });
  21. }
  22. }

Module实现类比较单一,就是获取Remote/Local数据,还有一些数据解析和数据封装操作也可以通过这层去实现,另外Android是UI线程非安全的,所以如果通过子线程获取Remote/Local数据,那么更新UI的操作必然不能直接进行,只能通过主线程去更新UI,所以这里讲Module和View分层也是很有道理的。

Presenter层(主要的业务逻辑层)

  1. public class WeatherPresenterImpl implements WeatherPresenter, OnWeatherListener {
  2. /*Presenter作为中间层,持有View和Model的引用*/
  3. private WeatherView weatherView;
  4. private WeatherModel weatherModel;
  5. public WeatherPresenterImpl(WeatherView weatherView) {
  6. this.weatherView = weatherView;
  7. weatherModel = new WeatherModelImpl();
  8. }
  9. @Override
  10. public void getWeather(String cityNO) {
  11. weatherView.showLoading();
  12. weatherModel.loadWeather(cityNO, this);
  13. }
  14. @Override
  15. public void onSuccess(Weather weather) {
  16. weatherView.hideLoading();
  17. weatherView.setWeatherInfo(weather);
  18. }
  19. @Override
  20. public void onError() {
  21. weatherView.hideLoading();
  22. weatherView.showError();
  23. }
  24. }

通过这个Presenter类可以看到仅仅有业务逻辑,而不会有UI和数据先关的东西在里面。同时也可以看出,这个类更多的是在桥接ViewModule层,并没有做其他多余的操作。另外Module层通过回调到Presenter,然后通过Presenter通知View层更新UI,这个回调就是上面的OnWeatherListener,当然如果想让这种耦合关系变得极其简单,可以通过EventBus处理。

前面说了很多MVP的好话,这里不得不给他破一点凉水,实际开发过程中会有个不大不小的坑。

Activity会在很多情况下被系统重启:
- 当用户旋转屏幕
- 在后台时内存不足
- 改变语言设置
- attache 一个外部显示器等。

一旦重启以后,Presenter如何处理成为一个问题,大体的处理思路如下图所示
此处输入图片的描述

前两种就不细说了,关于第三种处理方案可以详细参考 通过Loader延长Presenter生命周期

另外本文参考了 Android MVP 详解(上) 该文总结的很逆天,再次佩服作者的无私奉献精神。

附:
MVX Android设计架构浅析-MVC
MVX Android设计架构浅析-MVP
MVX Android设计架构浅析-MVVM

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注