MVP模式浅析及其简单运用

一、概述

MVP 全称:Model-View-Presenter ;MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方(Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。

二、MVP模式的三个角色

1、Presenter---交互中间人

Presenter主要作为沟通View与Model的桥梁,它从Model层检索数据后,返回给View层,使得View和Model之间没有耦合,也将业务逻辑从View的角色上抽离出来。

2、View---用户界面

View通常是指Activity、Fragment或者某个View控件,它含有一个Presenter的成员变量。通常View需要实现一个逻辑接口,将View上的操作转交给Presenter进行实现,最后Presenter调用View逻辑接口将结果返回给View元素。

3、Model---数据的存取

对于一个结构化的App来说,Model角色主要是提供数据的存取功能。Presenter需要通过Model层存储、获取数据,Model就像一个数据仓库。更直白的说Model就是封装了DAO或者网络获取数据的角色,或者两种数据获取方式的集合

MVP模式

三、MVC和MVP的区别?

MVP与MVC最主要的区别就是MVP中View和Model是不直接通信的,而是通过Presenter间接通信的,所有的交互都发生在Presenter内部;而MVC中View和Model层是直接通信的,View会直接从Model中读取数据而不是通过Controller,从而View里面会包含Model的信息,不可避免的还要包括一些业务逻辑。在MVC模型里更关注的是Model不变,而同时有多个对Model的不同显示(即View)。所以在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View是比较困难的,至少那些业务逻辑是无法重用的。
MVP就比较好的解决了这个问题,在MVP中,Presenter完全把Model和View进行了分离,主要的业务逻辑在Presenter里实现,而且Presenter与具体View是没有直接保持关联的,而是通过定义好的借口进行交互,从而使得在变更View的时候可以保持Presenter的不变,即重用。


MVC模式

四、MVP的优点

1、降低耦合度,Model和View完全分离,可以修改View而不影响Model。可能对于简单的应用来说MVP稍显麻烦,各种各样的接口与概念,使得整个应用充斥着零散的接口,但是它使你的应用变得复杂时依然能够保持清晰的结构,它能够非常好的组织应用结构,使应用变得灵活,拥抱变化。
2、Presenter可以复用,一个Presenter可以用于多个View,而不需要改变Presenter的逻辑(在View的变动不影响业务逻辑的前提下)。这个特性非常的有用,因为View的变化总是比模型的变化频繁。
3、模块划分明显,层次清晰
4、隐藏数据
5、利于测试驱动开发
6、View可以进行完全的组件化,在MVP当中,View不依赖于Model,这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务完全无知,它只需要提供一系列接口给上层接口,这样就可以做到高度可复用的View组件

五、MVP的缺点

1、可能造成内存泄漏。由于presenter经常性地需要执行耗时操作,例如请求网络数据。而Presenter一直持有会Activity对象的强引用,如果在请求结束之前使得Activity无法被回收,此时就发生了内存泄漏。这个我们可以通过弱引用和Activity、Fragment的生命周期来解决这个问题(在activity创建的时候建立view与presenter的绑定,在activity销毁时解除绑定)
2、Presenter中除了应用逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。
3、如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。
4、额外的代码复杂度以及学习成本。

六、MVP的简单运用

这里我们使用MVP模式实现了一个简单的加载图片的功能,抽取了BaseActivity和BasePresenter,使用了软引用,当系统内存不足时优先回收Model而不是View,这样有效的避免了内存泄漏,用户体验更好。

/**
 * T  对应着Activity 的UI抽象接口  视图
 */

public abstract class BasePersenter<T> {
    /**
     * 持有UI接口的弱引用
     */
    protected WeakReference<T> mViewRef;

    /**
     * 获取数据方法
     */
    public abstract void fectch();

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

    /**
     * 解绑
     */
    public void detach()
    {
        if(mViewRef!=null)
        {
            mViewRef.clear();
            mViewRef=null;
        }
    }
}
/**
 * presenter层
 */

public class GirlPresentV1<T>  extends  BasePersenter<IGrilView> {
    /**
     * 持有视图层 UI接口的引用  此时的视图层Activity
     */
    IGrilView mGrilView;

    public GirlPresentV1(IGrilView mGrilView) {
        this.mGrilView = mGrilView;
    }

    /**
     * 持有模型Model的引用
     */
    IGirlModel girlModel=new GirlModelImlV1();
    @Override
    public  void fectch()
    {
        mGrilView.showLoading();
        if(girlModel!=null)
        {
            /**
             * 参数为数据回调监听
             */
            girlModel.loadGirl(new IGirlModel.GirlOnLoadlitener() {
                @Override
                public void onComplete(List<Girl> girls) {
                    //回调视图层
                    mGrilView.showGrils(girls);
                }
            });
        }
    }
}
public interface IGrilView {
    /**
     * UI业务逻辑   加载进度条
     */
    void showLoading();

    void showGrils(List<Girl> grils);
}
/**
 * 监听数据返回
 */

public interface IGirlModel {
    void loadGirl(GirlOnLoadlitener girlOnLoadlitener);
    interface  GirlOnLoadlitener
    {
        void  onComplete(List<Girl> girls);
    }
}

/**
 * 模型层
 */

public class GirlModelImlV1  implements  IGirlModel {
    Handler handler=new Handler(Looper.getMainLooper());
    @Override
    public void loadGirl(final GirlOnLoadlitener girlOnLoadlitener) {
        new Thread()
        {
            @Override
            public void run() {
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                final List<Girl> data = new ArrayList<Girl>();
                data.add(new Girl(R.drawable.f1, "五颗星", "佰仟媚儿初夏新款韩版时尚潮流女个性字母印花无袖露脐上衣"));
                data.add(new Girl(R.drawable.f2, "四颗星", "迷依诗诺夏天新款韩版女装性感夜店欧美风字母印花牛仔露脐背心上衣"));
                data.add(new Girl(R.drawable.f3, "五颗星", "迷依诗诺春夏欧美新款性感女装单排扣牛仔背心露脐上衣"));
                data.add(new Girl(R.drawable.f4, "三颗星", "美莉丹 新款欧美单排扣牛仔背心露脐上衣"));
                data.add(new Girl(R.drawable.f5, "五颗星", "夏季新款韩版时尚个性字母无袖露脐上衣"));
                data.add(new Girl(R.drawable.f6, "三颗星", "新款欧美单排扣牛仔背心露脐上衣"));
                data.add(new Girl(R.drawable.f7, "四颗星", "夏装新款下摆波浪边挂脖露肩"));
                data.add(new Girl(R.drawable.f8, "五颗星", "夏天新款欧美时尚潮流休闲百"));
                data.add(new Girl(R.drawable.f9, "四颗星", "迷依诗诺夏季新款小香风甜美性感夜"));
                data.add(new Girl(R.drawable.f10, "三颗星", "安巴克夏季新款韩版时尚套装性感"));
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        //回调
                        girlOnLoadlitener.onComplete(data);
                    }
                });
            }
        }.start();

    }
}

* V  IGrilView接口
 *
 */
public abstract  class BaseActivty<V,T extends BasePersenter<V>> extends Activity {
    protected  T mPresent;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresent=createPresent();//创建Presenter
        mPresent.attachView((V) this);//View与Presenter建立关联
    }

   //activity销毁时取消View与presenter的关联
    @Override
    protected void onDestroy() {
        mPresent.detach();
        super.onDestroy();
    }

    /**
     * 子类实现具体的构建过程
     * @return
     */
    protected abstract T createPresent() ;
}
/**
 * 属于视图层
 * View
 */
public class  MainActivity extends BaseActivty<IGrilView,GirlPresentV1<IGrilView>> implements IGrilView{
    private ListView listView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_v1);
        listView= (ListView) findViewById(R.id.listview);
        mPresent.fectch();
    }
    @Override
    public void showLoading() {
        Toast.makeText(this,"正在拼命加载",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showGrils(List<Girl> grils) {
        listView.setAdapter(new GirlListAdapter(this,grils));
    }
    @Override
    protected GirlPresentV1<IGrilView> createPresent() {
        return new GirlPresentV1<>(this);
    }
}

本文参考阅读:
1、Android源码设计模式解析与实战
2、 http://baike.baidu.com/link?url=OmzJIKLueyG53pSFFnzr0C7mpj4LreQYvXzMILC_NthvSgv8_kwqG6JikmpU89TG8ca0SZLfpydxzLhuuiMVWq3VSfjtPGzpSOtdVU-nil_

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容