本帖最后由 杨顺发老师 于 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
- package himan.mvp.bean;
-
- import java.io.Serializable;
-
- public class NewsInfo implements Serializable {
-
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- private String datetime;
- private int id;
- private String title;
- private String imageUrl;
- private String abstractInfo;
-
- public String getDatetime() {
- return datetime;
- }
-
- public void setDatetime(String datetime) {
- this.datetime = datetime;
- }
-
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getImageUrl() {
- return imageUrl;
- }
-
- public void setImageUrl(String imageUrl) {
- this.imageUrl = imageUrl;
- }
-
- public String getAbstractInfo() {
- return abstractInfo;
- }
-
- public void setAbstractInfo(String abstractInfo) {
- this.abstractInfo = abstractInfo;
- }
-
- public NewsInfo(String datetime, int id, String title, String imageUrl,
- String abstractInfo) {
- super();
- this.datetime = datetime;
- this.id = id;
- this.title = title;
- this.imageUrl = imageUrl;
- this.abstractInfo = abstractInfo;
- }
-
- public NewsInfo() {
- }
-
- }
复制代码
Model实现类:
- package himan.mvp.model;
-
- import himan.mvp.bean.NewsInfo;
-
- import java.util.List;
-
- public interface INewsModel {
-
- /**
- * 加载数据
- *
- * @param dataListener
- */
- void loadNews(IOnDataListener<List<NewsInfo>> dataListener);
-
- /**
- * 缓存数据
- *
- * @param listNews
- */
- void saveNews(List<NewsInfo> listNews);
- }
复制代码 Model实现类:
- package himan.mvp.modelimpl;
-
- import java.util.List;
-
- import himan.mvp.bean.NewsInfo;
- import himan.mvp.model.INewsModel;
- import himan.mvp.model.IOnDataListener;
- import himan.mvp.net.NewsNetHelper;
- import himan.mvp.net.volley.UIDataListener;
-
- public class NewsModelImpl implements INewsModel {
-
- @Override
- public void loadNews(final IOnDataListener<List<NewsInfo>> dataListener) { // 回调接口
- NewsNetHelper netHelper = new NewsNetHelper();
- netHelper.setDataListener(new UIDataListener<List<NewsInfo>>() {
-
- @Override
- public void onDataChanged(List<NewsInfo> data) {
- if (data == null) {
- dataListener.error();
- } else {
- dataListener.completeLoad(data);
- }
- }
- });
- netHelper
- .doHttpGet("http://toutiao.com/api/article/recent/?source=2&count=20&category=__all__&max_behot_time=1449467901.41&utm_source=toutiao&offset=0&_=1449468004256");
- }
-
- @Override
- public void saveNews(List<NewsInfo> listNews) {
- // 缓存数据到本地
- }
-
- }
复制代码 View层接口:依赖于抽象编程:
- package himan.mvp.view;
-
- import himan.mvp.bean.NewsInfo;
-
- import java.util.List;
-
- public interface INewsView {
- /**
- * 显示新闻列表
- *
- * @param listNews
- */
- public void showNewsList(List<NewsInfo> listNews);
-
- /**
- * 显示正在加载中进度条
- */
- public void showLoading();
-
- /**
- * 关闭正在加载中进度条
- */
- public void hideLoading();
-
- /**
- * 显示数据加载失败
- */
- public void showError();
-
- }
复制代码
MVP关键之处:中间人Presenter - package himan.mvp.presenter;
-
- import java.util.List;
-
- import himan.mvp.bean.NewsInfo;
- import himan.mvp.model.INewsModel;
- import himan.mvp.model.IOnDataListener;
- import himan.mvp.modelimpl.NewsModelImpl;
- import himan.mvp.view.INewsView;
-
- public class NewsPresenter {
- // 同时持有Model层和View层的引用
- private INewsView mNewsView;
- private INewsModel mNewsModel = new NewsModelImpl();
-
- public NewsPresenter(INewsView newsView) {
- this.mNewsView = newsView;
- }
- public void showNews() {
- mNewsView.showLoading();
- mNewsModel.loadNews(new IOnDataListener<List<NewsInfo>>() {
-
- @Override
- public void error() {
- // 加载失败
- mNewsView.hideLoading();
- mNewsView.showError();
- }
-
- @Override
- public void completeLoad(List<NewsInfo> t) {
- mNewsView.hideLoading();
- mNewsView.showNewsList(t);
- }
- });
- }
-
- }
复制代码
Activity实现了View接口: - package himan.mvp;
-
- import himan.mvp.adapter.NewsAdapter;
- import himan.mvp.bean.NewsInfo;
- import himan.mvp.net.volley.VolleyQueueController;
- import himan.mvp.presenter.NewsPresenter;
- import himan.mvp.utils.LoadingWindow;
- import himan.mvp.view.INewsView;
-
- import java.util.ArrayList;
- import java.util.List;
-
- import android.app.Activity;
- import android.os.Bundle;
- import android.widget.ListView;
- import android.widget.Toast;
-
- import com.nostra13.universalimageloader.core.ImageLoader;
- import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
-
- /**
- * 测试 加载的数据 是从今日头条官网的直接push下来的
- *
- * @author Mr.Himan
- *
- */
- public class MainActivity extends Activity implements INewsView {
-
- private ListView mlVNews;
-
- private NewsAdapter mNewsAdapter;
-
- private List<NewsInfo> mListNews;
-
- private NewsPresenter mNewsPresenter;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- // 初始化Volley框架
- VolleyQueueController.init(this);
- // ImageLoader 默认配置参数
- ImageLoaderConfiguration configuration = ImageLoaderConfiguration
- .createDefault(this);
- // 初始化ImageLoader
- ImageLoader.getInstance().init(configuration);
- initView();
- mNewsPresenter = new NewsPresenter(this);
- mNewsPresenter.showNews();
-
- }
-
- private void initView() {
- mlVNews = (ListView) findViewById(R.id.news_lv_news_list);
- mListNews = new ArrayList<NewsInfo>();
- mNewsAdapter = new NewsAdapter(this, mListNews, R.layout.lv_item_news);
- mlVNews.setAdapter(mNewsAdapter);
- }
-
- @Override
- public void showNewsList(List<NewsInfo> listNews) {
- mListNews.addAll(listNews);
- mNewsAdapter.notifyDataSetChanged();
- }
-
- @Override
- public void showLoading() {
- Toast.makeText(this, "正在加载数据...", Toast.LENGTH_LONG).show();
- }
-
- @Override
- public void hideLoading() {
- Toast.makeText(this, "数据加载成功", Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void showError() {
-
- }
-
- }
复制代码
这就是MVP实现一个新闻展示的全部代码(貌似头条的接口关掉了 - -),Activity实现了View接口,而Presenter中保存有View和Model层的引用,使得Presenter能通过Model拿到数据,通过View的引用控制视图的显示。流程大概是这样,Presenter先通过Model的引用拿到数据,然后通过View的引用进行数据的显示,视图更新。Activity中的代码也非常简介。通过上面代码我们能够看出MVP的优点还是很多的,首先使得Model和View层完全解耦,我们知道项目中改动最多的一般都是View层,这就让程序易于维护,而且能够高度复用Presenter,而且也易于后期的拓展。缺点也显而易见,就是爆炸式增长的类和接口。复用的时候也可能造成接口的冗余。最大的问题是Presenter持有着View的强应用,在请求网络数据等耗时操作的时候,Activity可能被销毁,可能会导致View无法回收,而造成内存问题。
以上。 |