A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

1黑马币
本帖最后由 杨顺发老师 于 2016-1-31 10:51 编辑

最近这个MVP火了啊,默默无闻好几年,终于让捡起来了。来,看看同学的提问,


同学提问:
MVP在android中如何去使用?貌似最近这个概念挺火的,能不能结合实际例子来进行说明?发哥:

在讲MVP 之前,我们先来简单说下什么是MVC, 即Model(模型)、View(视图)、Control(控制器),相信大家对于MVC模式早已耳熟能详。原理性的东西这里不再多说。MVC在AndroidApp里面就有很好的体现。因为对于Android本身来说,界面部分的开发一般会用XML文件进行界面的描述开发。也就是MVC中的View层。而对于Model部分则大多是对应本地数据文件的读取或从网络获取数据。最后的Control控制器则有Activity来担当。MVC就说到这里,相信大多数Android猿们也是基于这种模式进行开发的。接下来,我们就来谈一谈今天的主角MVP模式在Android中又是如何实现的。

         何为MVP呢?即Model,VIew,Presenter(交互中间人相当于Control) 。但这里的Presenter隔离了Model和View之间的通信,使得Model和View之间能够完全解耦。在传统的MVC中,Activity担当了Control的角色,同时还要负责Dialog,Toast,PopupWindow的弹出,这就往往让Activity的责任变得繁重。一个Activity可能动不动就是几千行代码。而在MVP中View的角色不仅仅只是XML,而变成了Activity,Activity只负责View的工作。结合上面说的MVP能使Model和View之间能完全解耦,也就是说,在Activity中不会有任何关于Model层的操作,这就符合面向对象设计原则中的单一职责,让各个模块只负责自己的部分。更易于维护,代码也会更加简介。下面我会以一个小的项目来演示MVP在Android中如何使用。我在今日头天的web页拿到了一个新闻的URL,获取Json数据,解析显示

Bean


  1. package himan.mvp.bean;  
  2.    
  3. import java.io.Serializable;  
  4.    
  5. public class NewsInfo implements Serializable {  
  6.    
  7.     /**
  8.      *  
  9.      */  
  10.     private static final long serialVersionUID = 1L;  
  11.     private String datetime;  
  12.     private int id;  
  13.     private String title;  
  14.     private String imageUrl;  
  15.     private String abstractInfo;  
  16.    
  17.     public String getDatetime() {  
  18.         return datetime;  
  19.     }  
  20.    
  21.     public void setDatetime(String datetime) {  
  22.         this.datetime = datetime;  
  23.     }  
  24.    
  25.     public int getId() {  
  26.         return id;  
  27.     }  
  28.    
  29.     public void setId(int id) {  
  30.         this.id = id;  
  31.     }  
  32.    
  33.     public String getTitle() {  
  34.         return title;  
  35.     }  
  36.    
  37.     public void setTitle(String title) {  
  38.         this.title = title;  
  39.     }  
  40.    
  41.     public String getImageUrl() {  
  42.         return imageUrl;  
  43.     }  
  44.    
  45.     public void setImageUrl(String imageUrl) {  
  46.         this.imageUrl = imageUrl;  
  47.     }  
  48.    
  49.     public String getAbstractInfo() {  
  50.         return abstractInfo;  
  51.     }  
  52.    
  53.     public void setAbstractInfo(String abstractInfo) {  
  54.         this.abstractInfo = abstractInfo;  
  55.     }  
  56.    
  57.     public NewsInfo(String datetime, int id, String title, String imageUrl,  
  58.             String abstractInfo) {  
  59.         super();  
  60.         this.datetime = datetime;  
  61.         this.id = id;  
  62.         this.title = title;  
  63.         this.imageUrl = imageUrl;  
  64.         this.abstractInfo = abstractInfo;  
  65.     }  
  66.    
  67.     public NewsInfo() {  
  68.     }  
  69.    
  70. }
复制代码



Model实现类:


  1. package himan.mvp.model;  
  2.    
  3. import himan.mvp.bean.NewsInfo;  
  4.    
  5. import java.util.List;  
  6.    
  7. public interface INewsModel {  
  8.    
  9.     /**
  10.      * 加载数据
  11.      *  
  12.      * @param dataListener
  13.      */  
  14.     void loadNews(IOnDataListener<List<NewsInfo>> dataListener);  
  15.    
  16.     /**
  17.      * 缓存数据
  18.      *  
  19.      * @param listNews
  20.      */  
  21.     void saveNews(List<NewsInfo> listNews);  
  22. }
复制代码
Model实现类:

  1. package himan.mvp.modelimpl;  
  2.    
  3. import java.util.List;  
  4.    
  5. import himan.mvp.bean.NewsInfo;  
  6. import himan.mvp.model.INewsModel;  
  7. import himan.mvp.model.IOnDataListener;  
  8. import himan.mvp.net.NewsNetHelper;  
  9. import himan.mvp.net.volley.UIDataListener;  
  10.    
  11. public class NewsModelImpl implements INewsModel {  
  12.    
  13.     @Override  
  14.     public void loadNews(final IOnDataListener<List<NewsInfo>> dataListener) {  // 回调接口  
  15.         NewsNetHelper netHelper = new NewsNetHelper();  
  16.         netHelper.setDataListener(new UIDataListener<List<NewsInfo>>() {  
  17.    
  18.             @Override  
  19.             public void onDataChanged(List<NewsInfo> data) {  
  20.                 if (data == null) {  
  21.                     dataListener.error();  
  22.                 } else {  
  23.                     dataListener.completeLoad(data);  
  24.                 }  
  25.             }  
  26.         });  
  27.         netHelper  
  28.                 .doHttpGet("http://toutiao.com/api/article/recent/?source=2&count=20&category=__all__&max_behot_time=1449467901.41&utm_source=toutiao&offset=0&_=1449468004256");  
  29.     }  
  30.    
  31.     @Override  
  32.     public void saveNews(List<NewsInfo> listNews) {  
  33.         // 缓存数据到本地  
  34.     }  
  35.    
  36. }
复制代码
View层接口:依赖于抽象编程:
  1. package himan.mvp.view;  
  2.    
  3. import himan.mvp.bean.NewsInfo;  
  4.    
  5. import java.util.List;  
  6.    
  7. public interface INewsView {  
  8.     /**
  9.      * 显示新闻列表
  10.      *  
  11.      * @param listNews
  12.      */  
  13.     public void showNewsList(List<NewsInfo> listNews);  
  14.    
  15.     /**
  16.      * 显示正在加载中进度条
  17.      */  
  18.     public void showLoading();  
  19.    
  20.     /**
  21.      * 关闭正在加载中进度条
  22.      */  
  23.     public void hideLoading();  
  24.       
  25.     /**
  26.      * 显示数据加载失败
  27.      */  
  28.     public void showError();  
  29.       
  30. }
复制代码

MVP关键之处:中间人Presenter
  1. package himan.mvp.presenter;  
  2.    
  3. import java.util.List;  
  4.    
  5. import himan.mvp.bean.NewsInfo;  
  6. import himan.mvp.model.INewsModel;  
  7. import himan.mvp.model.IOnDataListener;  
  8. import himan.mvp.modelimpl.NewsModelImpl;  
  9. import himan.mvp.view.INewsView;  
  10.    
  11. public class NewsPresenter {  
  12.          // 同时持有Model层和View层的引用  
  13.     private INewsView mNewsView;  
  14.     private INewsModel mNewsModel = new NewsModelImpl();  
  15.    
  16.     public NewsPresenter(INewsView newsView) {  
  17.         this.mNewsView = newsView;  
  18.     }  
  19.     public void showNews() {  
  20.         mNewsView.showLoading();  
  21.         mNewsModel.loadNews(new IOnDataListener<List<NewsInfo>>() {  
  22.    
  23.             @Override  
  24.             public void error() {  
  25.                 // 加载失败  
  26.                 mNewsView.hideLoading();  
  27.                 mNewsView.showError();  
  28.             }  
  29.    
  30.             @Override  
  31.             public void completeLoad(List<NewsInfo> t) {  
  32.                 mNewsView.hideLoading();  
  33.                 mNewsView.showNewsList(t);  
  34.             }  
  35.         });  
  36.     }  
  37.    
  38. }
复制代码

Activity实现了View接口:
  1. package himan.mvp;  
  2.    
  3. import himan.mvp.adapter.NewsAdapter;  
  4. import himan.mvp.bean.NewsInfo;  
  5. import himan.mvp.net.volley.VolleyQueueController;  
  6. import himan.mvp.presenter.NewsPresenter;  
  7. import himan.mvp.utils.LoadingWindow;  
  8. import himan.mvp.view.INewsView;  
  9.    
  10. import java.util.ArrayList;  
  11. import java.util.List;  
  12.    
  13. import android.app.Activity;  
  14. import android.os.Bundle;  
  15. import android.widget.ListView;  
  16. import android.widget.Toast;  
  17.    
  18. import com.nostra13.universalimageloader.core.ImageLoader;  
  19. import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;  
  20.    
  21. /**
  22. * 测试 加载的数据 是从今日头条官网的直接push下来的
  23. *  
  24. * @author Mr.Himan
  25. *  
  26. */  
  27. public class MainActivity extends Activity implements INewsView {  
  28.    
  29.     private ListView mlVNews;  
  30.    
  31.     private NewsAdapter mNewsAdapter;  
  32.    
  33.     private List<NewsInfo> mListNews;  
  34.    
  35.     private NewsPresenter mNewsPresenter;  
  36.    
  37.     @Override  
  38.     protected void onCreate(Bundle savedInstanceState) {  
  39.         super.onCreate(savedInstanceState);  
  40.         setContentView(R.layout.activity_main);  
  41.         // 初始化Volley框架  
  42.         VolleyQueueController.init(this);  
  43.         // ImageLoader 默认配置参数  
  44.         ImageLoaderConfiguration configuration = ImageLoaderConfiguration  
  45.                 .createDefault(this);  
  46.         // 初始化ImageLoader  
  47.         ImageLoader.getInstance().init(configuration);  
  48.         initView();  
  49.         mNewsPresenter = new NewsPresenter(this);  
  50.         mNewsPresenter.showNews();  
  51.    
  52.     }  
  53.    
  54.     private void initView() {  
  55.         mlVNews = (ListView) findViewById(R.id.news_lv_news_list);  
  56.         mListNews = new ArrayList<NewsInfo>();  
  57.         mNewsAdapter = new NewsAdapter(this, mListNews, R.layout.lv_item_news);  
  58.         mlVNews.setAdapter(mNewsAdapter);  
  59.     }  
  60.    
  61.     @Override  
  62.     public void showNewsList(List<NewsInfo> listNews) {  
  63.         mListNews.addAll(listNews);  
  64.         mNewsAdapter.notifyDataSetChanged();  
  65.     }  
  66.    
  67.     @Override  
  68.     public void showLoading() {  
  69.         Toast.makeText(this, "正在加载数据...", Toast.LENGTH_LONG).show();  
  70.     }  
  71.    
  72.     @Override  
  73.     public void hideLoading() {  
  74.         Toast.makeText(this, "数据加载成功", Toast.LENGTH_SHORT).show();  
  75.     }  
  76.    
  77.     @Override  
  78.     public void showError() {  
  79.    
  80.     }  
  81.    
  82. }
复制代码

这就是MVP实现一个新闻展示的全部代码(貌似头条的接口关掉了 - -),Activity实现了View接口,而Presenter中保存有View和Model层的引用,使得Presenter能通过Model拿到数据,通过View的引用控制视图的显示。流程大概是这样,Presenter先通过Model的引用拿到数据,然后通过View的引用进行数据的显示,视图更新。Activity中的代码也非常简介。通过上面代码我们能够看出MVP的优点还是很多的,首先使得Model和View层完全解耦,我们知道项目中改动最多的一般都是View层,这就让程序易于维护,而且能够高度复用Presenter,而且也易于后期的拓展。缺点也显而易见,就是爆炸式增长的类和接口。复用的时候也可能造成接口的冗余。最大的问题是Presenter持有着View的强应用,在请求网络数据等耗时操作的时候,Activity可能被销毁,可能会导致View无法回收,而造成内存问题。



以上。

2 个回复

正序浏览
发哥,你的最后一段话“最大的问题是Presenter持有着View的强应用,在请求网络数据等耗时操作的时候,Activity可能被销毁,可能会导致View无法回收,而造成内存问题。”这个怎么理解?
回复 使用道具 举报
沙发,谢谢。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马