MVP架构实践

一.MVP理论简介

1.为何要在android中引入MVP

??在Android项目中,Activity和Fragment占据了大部分的开发工作。而MVP设计模式可以优化Activity和Fragment的代码。

??相信很多人阅读代码的时候,都是从Activity开始的,对着一个1000+行代码的Activity,看了都觉得难受。

??使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多。

MVP模式优点:

  • 分离了视图逻辑和业务逻辑,降低了耦合
  • Activity只处理生命周期的任务,代码变得更加简洁
  • 视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性
  • Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试
  • 把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存泄露和OOM

2.MVP是什么

View 对应于Activity,负责View的绘制以及与用户交互。

Model 依然是业务逻辑和实体模型。一般封装了数据库DAO或者网络获取数据的角色,或者两种数据获取方式的集合。

Presenter 负责完成View于Model间的交互。

MVP会解除View和Model的耦合,同时又带来了良好的可扩展性、可测试性,保证了系统的整洁性和灵活性。

MVP可以分离显示层和逻辑层,他们之间通过接口进行通信,减低耦合。理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为他们之间并不依赖具体,而是依赖于抽象。这使得Presenter可以运用于任何实现了View逻辑接口的UI,使之具有更广泛的适用性,保证了灵活性。

3.MVP与MVC的区别

??其实最明显的区别就是,MVC中是允许Model和View进行交互的,而MVP中很明显,Model与View之间的交互由Presenter完成。还有一点就是Presenter与View之间的交互是通过接口的(代码中会体现)。

4.android中的MVP实现方案

方案1:MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。这就是MVP模式,现在这样的话,Activity的工作的简单了,只用来响应生命周期,其他工作都丢到Presenter中去完成。

方案2:Activity作为Presenter,也有这种方案,不常见,也不易理解,这里不讲解。以下代码也主要以方案1为主。

二.android MVP实战demo

1.入门demo

public class WeatherEntity {

    /**
     * error : 0
     * status : success
     * date : 2016-07-18
     * results : [{"currentCity":"花都","pm25":"","index":[{"title":"穿衣","zs":"炎热","tipt":"穿衣指数","des":"天气炎热,建议着短衫、短裙、短裤、薄型T恤衫等清凉夏季服装。"},{"title":"洗车","zs":"较适宜","tipt":"洗车指数","des":"较适宜洗车,未来一天无雨,风力较小,擦洗一新的汽车至少能保持一天。"},{"title":"旅游","zs":"一般","tipt":"旅游指数","des":"天气较好,同时有微风相伴,但温度较高,天气热,请尽量避免高温时段外出,若外出请注意防暑降温和防晒。"},{"title":"感冒","zs":"少发","tipt":"感冒指数","des":"各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。"},{"title":"运动","zs":"较适宜","tipt":"运动指数","des":"天气较好,户外运动请注意防晒。推荐您进行室内运动。"},{"title":"紫外线强度","zs":"中等","tipt":"紫外线强度指数","des":"属中等强度紫外线辐射天气,外出时建议涂擦SPF高于15、PA+的防晒护肤品,戴帽子、太阳镜。"}],"weather_data":[{"date":"周一 07月18日 (实时:33℃)","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/qing.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"晴转多云","wind":"微风","temperature":"35 ~ 27℃"},{"date":"周二","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"多云","wind":"微风","temperature":"35 ~ 27℃"},{"date":"周三","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"多云","wind":"微风","temperature":"35 ~ 27℃"},{"date":"周四","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/qing.png","weather":"多云转晴","wind":"微风","temperature":"35 ~ 27℃"}]}]
     */

    private int error;
    private String status;
    private String date;
    /**
     * currentCity : 花都
     * pm25 :
     * index : [{"title":"穿衣","zs":"炎热","tipt":"穿衣指数","des":"天气炎热,建议着短衫、短裙、短裤、薄型T恤衫等清凉夏季服装。"},{"title":"洗车","zs":"较适宜","tipt":"洗车指数","des":"较适宜洗车,未来一天无雨,风力较小,擦洗一新的汽车至少能保持一天。"},{"title":"旅游","zs":"一般","tipt":"旅游指数","des":"天气较好,同时有微风相伴,但温度较高,天气热,请尽量避免高温时段外出,若外出请注意防暑降温和防晒。"},{"title":"感冒","zs":"少发","tipt":"感冒指数","des":"各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。"},{"title":"运动","zs":"较适宜","tipt":"运动指数","des":"天气较好,户外运动请注意防晒。推荐您进行室内运动。"},{"title":"紫外线强度","zs":"中等","tipt":"紫外线强度指数","des":"属中等强度紫外线辐射天气,外出时建议涂擦SPF高于15、PA+的防晒护肤品,戴帽子、太阳镜。"}]
     * weather_data : [{"date":"周一 07月18日 (实时:33℃)","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/qing.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"晴转多云","wind":"微风","temperature":"35 ~ 27℃"},{"date":"周二","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"多云","wind":"微风","temperature":"35 ~ 27℃"},{"date":"周三","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"多云","wind":"微风","temperature":"35 ~ 27℃"},{"date":"周四","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/qing.png","weather":"多云转晴","wind":"微风","temperature":"35 ~ 27℃"}]
     */

    private List<ResultsEntity> results;

    public void setError(int error) {
        this.error = error;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public void setResults(List<ResultsEntity> results) {
        this.results = results;
    }

    public int getError() {
        return error;
    }

    public String getStatus() {
        return status;
    }

    public String getDate() {
        return date;
    }

    public List<ResultsEntity> getResults() {
        return results;
    }

    public static class ResultsEntity {
        private String currentCity;
        private String pm25;
        /**
         * title : 穿衣
         * zs : 炎热
         * tipt : 穿衣指数
         * des : 天气炎热,建议着短衫、短裙、短裤、薄型T恤衫等清凉夏季服装。
         */

        private List<IndexEntity> index;
        /**
         * date : 周一 07月18日 (实时:33℃)
         * dayPictureUrl : http://api.map.baidu.com/images/weather/day/qing.png
         * nightPictureUrl : http://api.map.baidu.com/images/weather/night/duoyun.png
         * weather : 晴转多云
         * wind : 微风
         * temperature : 35 ~ 27℃
         */

        private List<WeatherDataEntity> weather_data;

        public void setCurrentCity(String currentCity) {
            this.currentCity = currentCity;
        }

        public void setPm25(String pm25) {
            this.pm25 = pm25;
        }

        public void setIndex(List<IndexEntity> index) {
            this.index = index;
        }

        public void setWeather_data(List<WeatherDataEntity> weather_data) {
            this.weather_data = weather_data;
        }

        public String getCurrentCity() {
            return currentCity;
        }

        public String getPm25() {
            return pm25;
        }

        public List<IndexEntity> getIndex() {
            return index;
        }

        public List<WeatherDataEntity> getWeather_data() {
            return weather_data;
        }

        public static class IndexEntity {
            private String title;
            private String zs;
            private String tipt;
            private String des;

            public void setTitle(String title) {
                this.title = title;
            }

            public void setZs(String zs) {
                this.zs = zs;
            }

            public void setTipt(String tipt) {
                this.tipt = tipt;
            }

            public void setDes(String des) {
                this.des = des;
            }

            public String getTitle() {
                return title;
            }

            public String getZs() {
                return zs;
            }

            public String getTipt() {
                return tipt;
            }

            public String getDes() {
                return des;
            }
        }

        public static class WeatherDataEntity {
            private String date;
            private String dayPictureUrl;
            private String nightPictureUrl;
            private String weather;
            private String wind;
            private String temperature;

            public void setDate(String date) {
                this.date = date;
            }

            public void setDayPictureUrl(String dayPictureUrl) {
                this.dayPictureUrl = dayPictureUrl;
            }

            public void setNightPictureUrl(String nightPictureUrl) {
                this.nightPictureUrl = nightPictureUrl;
            }

            public void setWeather(String weather) {
                this.weather = weather;
            }

            public void setWind(String wind) {
                this.wind = wind;
            }

            public void setTemperature(String temperature) {
                this.temperature = temperature;
            }

            public String getDate() {
                return date;
            }

            public String getDayPictureUrl() {
                return dayPictureUrl;
            }

            public String getNightPictureUrl() {
                return nightPictureUrl;
            }

            public String getWeather() {
                return weather;
            }

            public String getWind() {
                return wind;
            }

            public String getTemperature() {
                return temperature;
            }
        }
    }
}

public abstract class BasePresenter<T> {

    protected Reference<T> mViewRef;

    public void attachView(T view) {
        mViewRef = new WeakReference<T>(view);
    }

    public boolean isViewAttached() {
        return mViewRef != null && mViewRef.get() != null;
    }

    public void detachView() {
        if (mViewRef != null) {
            mViewRef.clear();
            mViewRef = null;
        }
    }

}

public class WeatherPresenter extends BasePresenter<WeatherView> {

    public void fetchList() {
        mViewRef.get().onShowLoading();
        VolleyNetHelper.getInstance().doPost(new BaseRequest() {
            @Override
            public String getMobileApi() {
                return "http://api.map.baidu.com/telematics/v3/weather?location=guangzhou&output=json&ak=B95329fb7fdda1e32ba3e3a245193146";
            }

            @Override
            public Map<String, String> getParams() {
                return null;
            }
        }, new BaseResponse<WeatherEntity>() {
            @Override
            public void onSuccess(WeatherEntity o) {
                mViewRef.get().onHideLoading();
                mViewRef.get().onFetchDataSuccess(o);
            }

            @Override
            public void onError(String msg) {
                mViewRef.get().onHideLoading();
                mViewRef.get().onFetchDataError(msg);
            }

            @Override
            public Class<WeatherEntity> getResponseClass() {
                return WeatherEntity.class;
            }
        });

    }

}
public interface WeatherView {

    void onFetchDataSuccess(WeatherEntity entity);

    void onShowLoading();

    void onHideLoading();

    void onFetchDataError(String msg);
}
public class WeahterActivity extends BaseActivity implements WeatherView {

    @InjectView(R.id.weather_tv)
    TextView mWeatherTv;
    private WeatherPresenter mWeatherPresenter;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_weather;
    }

    @Override
    protected void init(Bundle savedInstanceState) {
        mWeatherPresenter = new WeatherPresenter();
        mWeatherPresenter.attachView(this);
        mWeatherPresenter.fetchList();
    }

    @Override
    public void onFetchDataSuccess(WeatherEntity entity) {
        mWeatherTv.setText(entity.getDate());
    }

    @Override
    public void onShowLoading() {
        super.mLoadingDialog.showLoading(LoadingDialog.NETWORK_LOADING);
    }

    @Override
    public void onHideLoading() {
        super.mLoadingDialog.hideLoading();
    }

    @Override
    public void onFetchDataError(String msg) {
        ToastUtils.longShow(msg);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mWeatherPresenter.detachView();
    }
}

其他入门demo

登陆案例:https://segmentfault.com/a/1190000003927200

天气预报案例:http://rocko.xyz/2015/02/06/Android%E4%B8%AD%E7%9A%84MVP/

2.项目级demo

https://github.com/maoruibin/GankDaily

http://p.codekk.com/detail/Android/gzsll/TLint

这里个人感觉TLint的MVP实践要更好一些。有需要的可以直接去看源码。

三.一些问题和难点

这里针对方案1:

  1. 例如当应用进入后台且内存不足的时候,系统是会回收这个Activity的。通常我们都知道要用OnSaveInstanceState()去保存状态,用OnRestoreInstanceState()去恢复状态。 但是在我们的MVP中,View层是不应该去直接操作Model的,这样做不合理,同时也增大了M与V的耦合。
  2. 界面复用问题。通常我们在APP最初版本中是无法预料到以后会有什么变动的,例如我们最初使用一个Fragment去作为界面的显示,后来在版本变动中发现这个Fragment越来越庞大,而Fragment的生命周期又太过复杂造成很多难以理解的BUG,我们需要把这个界面放到一个Activity中实现。这时候就麻烦了,要把Fragment转成Activity,这可不仅仅是改改类名的问题,更多的是一大堆生命周期需要去修改。例如参考文章2中的译者就遇到过这样的问题。
  3. Activity本身就是Android中的一个Context。不论怎么去封装,都难以避免将业务逻辑代码写入到其中。
  4. 如何防止activity等等内存泄露。这个问题非常严重,好好想一下有无好的方案。
  5. V,P对应的比例关系。

四.参考资料

https://segmentfault.com/a/1190000003927200

http://blog.csdn.net/lmj623565791/article/details/46596109

http://www.kymjs.com/code/2015/11/09/01

http://www.jianshu.com/p/9a6845b26856

https://github.com/122627018/BaseMVP

http://m.h5.com.cn/news/anzhuo/19287.html

时间: 2024-09-30 09:26:33

MVP架构实践的相关文章

MVP架构。。。。

Model-View-Presenter(MVP)概述    MVC模式已经出现了几十年了,在GUI领域已经得到了广泛的应用,由于微软ASP.NET MVC Framework的出现,致使MVC一度成为.NET社区的热名话题.作为MVC的变种MVP模式,也已经出现好几年了,在微软模式与实践小组提供的Web Client Software Factory中,给出了实现MVP模式的应用程序最佳实践,本文将试着对这两种实现比较一二.MVC(Model-View-Controller,模型-视图-控制器

浅谈MVP架构及开发模式

Model-View-Presenter(MVP)概述    MVC模式已经出现了几十年了,在GUI领域已经得到了广泛的应用,由于微软ASP.NET MVC Framework的出现,致使MVC一度成为.NET社区的热名话题.作为MVC的变种MVP模式,也已经出现好几年了,在微软模式与实践小组提供的Web Client Software Factory中,给出了实现MVP模式的应用程序最佳实践,本文将试着对这两种实现比较一二.MVC(Model-View-Controller,模型-视图-控制器

mvp架构模式

今天是国庆节,祝大家节日快乐,愿祖国越发繁荣昌盛.假期程序员也不能偷懒,更新一些博文吧. 看到封面图片喜欢NBA的人可能很容易就想到了最有价值球员.但是此mvp非彼MVP,此mvp指的是现在Android开发中比较常见的一种软件架构模式.mvp架构模式是Google官方推荐的架构模式,特别是近年来的新项目,mvp+retrofit+rxjava+dragger2配合使用已经在引领程序界的潮流了,在github上可以轻易的搜到一大堆这样的开源项目.前端时间笔者也在公司的一个sdk上进行了尝试,在此

Android中的MVP架构初探

说来惭愧,MVP的架构模式已经在Android领域出现一两年了,但是到今天自己才开始Android领域中的MVP架构征程.闲话不多说,开始吧! 一.架构演变概述 我记得我找第一份工作时,面试官问我"android是否属于MVC架构模式,简述一下".确实,Android的整体设计结构就是MVC的设计模式,在J2EE的开发中,使用的也是MVC模式,MVC模式是一个经典,经历了几十年的考验.Android项目中的MVC架构: View:是应用程序中处理数据显示的部分,对应于layout文件下

Android MVP架构分析

App架构在Android开发者中一直是讨论比较多的一个话题,目前讨论较多的有MVP.MVVM.Clean这三种.google官方对于架构的态度一直是非常开放的,让开发者自主选择组织和架构app的方式,期望能留给开发者更多的灵活性. 由于没有一套权威的架构实现,现在很多App项目中在架构方面都有或多或少的问题.第一种常见问题是没有架构,需求中的一个页面对应项目中的一个activity或一个fragment,所有的界面响应代码.业务逻辑代码.数据请求代码等等都集中在其中.第二种常见的问题是架构实现

如何使用MVP架构Android应用项目

目录 MVP简介 MVP结构 MVP与MVC区别 实战演习 正文 1.MVP简介 相信大家对MVC都是比较熟悉了:M-Model-模型.V-View-视图.C-Controller-控制器,MVP作为MVC的演化版本,那么类似的MVP所对应的意义:M-Model-模型.V-View-视图.P-Presenter-表示器.从MVC和MVP两者结合来看,Controlller/Presenter在MVC/MVP中都起着逻辑控制处理的角色,起着控制各业务流程的作用.而MVP与MVC最不同的一点是M与V

Android官方MVP架构示例项目解析

前段时间Google在Github推出了一个项目,专门展示Android引用各种各样的MVP架构,算是官方教程了.趁着还新鲜,让我们来抛砖引玉一探究竟,看看在Google眼里什么样算是好的MVP架构. App架构在Android开发者中一直是讨论比较多的一个话题,目前讨论较多的有MVP.MVVM.Clean这三种.google官方对于架构的态度一直是非常开放的,让开发者自主选择组织和架构app的方式,期望能留给开发者更多的灵活性. 由于没有一套权威的架构实现,现在很多App项目中在架构方面都有或

MVP架构在xamarin android中的简单使用

好几个月没写文章了,使用xamarin android也快接近两年,还有一个月职业生涯就到两个年了,从刚出来啥也不会了,到现在回头看这个项目,真jb操蛋(真辛苦了实施的人了,无数次吐槽怎么这么丑),怪自己太年轻了,还好是给指定行业的人使用. 重新学习思考之后,再看自己在项目中的某些实现的代码,的确不尽人意,甚至想骂自己. 项目经常改,改来该去,代码一直增加,一个fragment也没什么功能,接近1000行的代码,用region括起来,开看起来还挺整齐的,找的时候就凉了.究其原因,没有一种模式,所

中小研发团队架构实践之系列大纲

以下是中小研发团队架构实践系列的大纲,部分已链接,未链接部分我也会持续的更新和发布,期待你的支持与互动. 第一篇 开篇--照着做,你也能成为架构师 第1章 中小研发团队架构实践,附案例和代码 一.框架篇--工欲善其事,必先利其器 二.架构篇--思想提升 三.公共应用篇--业务与技术的结合 四.进阶篇--从架构到管理 五.案例参考和Demo下载 第二篇 架构篇--思想提升第2章 企业总体架构规划 一.企业商务模型 二.架构现状 2.1 功能架构 2.2 应用架构 2.3 数据设计 2.4 物理架构