MVP架构

一年前,接触了自己的第一个MVP的Android项目,当时仔细研究过一段时间这种架构,但是后来一直负责的项目都是基于MVC,现在都要忘记了,现在把他记录下来。

MVP架构

  • Model-View-Persenter
    是IBM开发的一套适用C++,Java的编程模型。2000年出现。

  • Persenter/Controller:负责处理业务逻辑交互。
    View:负责显示视图。Model:负责提供数据。

  • 架构是对客观不足的妥协。规范是对主观不足的妥协。

MVP
Demo地址戳这里👇

图片 1

MVP

什么是MVP

Google-samples中看到,有很大篇幅是关于MVP,那么到底什么是MVP开发模式呢?

  1. Model: 数据层
  2. View: 视图层
  3. Presenter: 控制层(与Controller有什么区别?)

一、介绍

  MVP(Model View Presenter)架构是从著名的MVC(Model View
Controller)架构演变而来的。对于在Android应用中开发就可以视为是MVC架构,布局文件视为View,Activity视为Controller,但是Activity还要控制布局的更新,所以说Activity是Controller与View的合体,这样的结果是Actiivty的代码很多也很杂乱,而MVP就是将Activity充当的Controller与View的角色分割开来。
  View:对于View层也是视图层,在View层中只负责对数据的展示,提供友好的界面与用户进行交互。在Android开发中通常将Activity或者Fragment作为View层。
  Model:对于Model层也是数据层。它区别于MVC中的Model,在这里不仅仅只是数据模型。在MVP架构中Model它负责对数据的存取操作,例如对数据的读取,网络的数据请求等。
  Presenter:对于Presenter层也是连接View层与Model层的桥梁并对业务逻辑进行处理。在MVP架构中Model与View无法直接进行交互。所以在Presenter层它会从Model层获得所需要的数据,进行一些适当的处理后交由View层进行显示,这样通过Presenter将View与Model进行隔离,使得View和Model之间不存在耦合,同事也将业务逻辑从View中抽离。

  在MVP架构中将这三层分别抽象到各自的接口当中。通过接口将层次之间进行隔离,而Presenter对View和Model的相互依赖也是依赖于各自的接口。这点符合了接口隔离原则,也正是面向接口编程。在Presenter层中包含了一个View接口,并且依赖于Model接口,从而将Model层与View层联系在一起。而对于View层会持有一个Presnter成员变量并且只保留对Presenter接口的调用,具体业务逻辑全部交由Presnter接口实现类中处理。

一、介绍

  MVP(Model View Presenter)架构是从著名的MVC(Model View
Controller)架构演变而来的。对于在Android应用中开发就可以视为是MVC架构,布局文件视为View,Activity视为Controller,但是Activity还要控制布局的更新,所以说Activity是Controller与View的合体,这样的结果是Actiivty的代码很多也很杂乱,而MVP就是将Activity充当的Controller与View的角色分割开来。
  View:对于View层也是视图层,在View层中只负责对数据的展示,提供友好的界面与用户进行交互。在Android开发中通常将Activity或者Fragment作为View层。
  Model:对于Model层也是数据层。它区别于MVC中的Model,在这里不仅仅只是数据模型。在MVP架构中Model它负责对数据的存取操作,例如对数据的读取,网络的数据请求等。
  Presenter:对于Presenter层也是连接View层与Model层的桥梁并对业务逻辑进行处理。在MVP架构中Model与View无法直接进行交互。所以在Presenter层它会从Model层获得所需要的数据,进行一些适当的处理后交由View层进行显示,这样通过Presenter将View与Model进行隔离,使得View和Model之间不存在耦合,同事也将业务逻辑从View中抽离。

  在MVP架构中将这三层分别抽象到各自的接口当中。通过接口将层次之间进行隔离,而Presenter对View和Model的相互依赖也是依赖于各自的接口。这点符合了接口隔离原则,也正是面向接口编程。在Presenter层中包含了一个View接口,并且依赖于Model接口,从而将Model层与View层联系在一起。而对于View层会持有一个Presnter成员变量并且只保留对Presenter接口的调用,具体业务逻辑全部交由Presnter接口实现类中处理。

什么叫MVP,为什么要用MVP?

15年实习的时候,很幸运开始从0开始做第一个项目,老大把框架搭建起来,后来开始写业务逻辑代码,当时所在的公司是做电商的,需求那就是3个字,改,改,改,Activity的代码上千行已经习以为常,这样的代码变得非常臃肿,难以维护。

那么什么叫MVP?就是Modle——View——Presenter,看到这个概念很懵逼,先甩一张图看一下和普通MVC的区别:

图片 2image

从图中可以很明显的看到MVC和MVP的区别,MVP消除了View和Model之间的相互依赖,中间通过Presenter来通讯,解耦合。

MVC和MVP两个单词只差了一个字母,但是两种处理方式上改变得太多。

MVP角色

  • View :负责绘制UI元素、与用户进行交互(在Android中体现为Activity)。

  • Activity interface: 需要View实现的接口,View通过View
    interface与Presenter进行交互,降低耦合,方便进行单元测试。

  • Model:负责存储、检索、操纵数据(有时也实现一个Model
    interface用来降低耦合)。

  • Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。

图片 3

MVP架构设计

MVP工作原理

图片 4

MVP基础架构.png

二、好处

  1.View与Model并不直接进行交互,而是通过Presenter连接彼此。View中不存在Model,从而也不会存在业务逻辑。
  2.Presenter与View的交互式通过接口来实现的,耦合度低,也有利于单元测试。
  3.Presenter是基于行为的,一个Presnter可用于多个View,增强了代码复用。

二、好处

  1.View与Model并不直接进行交互,而是通过Presenter连接彼此。View中不存在Model,从而也不会存在业务逻辑。
  2.Presenter与View的交互式通过接口来实现的,耦合度低,也有利于单元测试。
  3.Presenter是基于行为的,一个Presnter可用于多个View,增强了代码复用。

MVC三个部分分别在干什么

  • View:对应于布局文件,但是细细的想一想这个View对应于布局文件,其实能做的事情特别少,实际上关于布局文件中的数据绑定的操作,事件处理的操作都在Activity中,造成了Activity既像View又像Controller。
  • Model:业务逻辑和实体模型
  • Controller:对应于Activity

如何实现MVP模式

  • View
    层通过接口实现UI业务逻辑。比如加载进度条,展示返回数据列表。此时Activity
    属于视图层。

图片 5

  • Model层负责存储、检索、操纵数据(有时也实现一个Model
    interface用来降低耦合)。一般抽象出来。需要在Persenter层实现
![](https://upload-images.jianshu.io/upload_images/325120-418ff10ed1885e55.png)
  • Persenter层作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。持有视图层
    UI 接口的引用。 同时持有Model的引用。所以此时需要调用视图层的逻辑。

图片 6

  • 最终在MainActivity 中初始化Persenter,并调用相应的方法。

图片 7

  • 因为项目中有很多Persenter,那么最好抽象出父类出来。为了避免内存泄漏的问题,对View使用弱引用的方式。
![](https://upload-images.jianshu.io/upload_images/325120-351470dece8b83be.png)
  • 另外Persenter的注入,最好放在baseActivity 中实现,避免重复代码。
![](https://upload-images.jianshu.io/upload_images/325120-b942bf0b90118ef1.png)

MVP实现UML

图片 8

MVP实现UML.png

三、使用

  使用MVP架构来实现一个ListView的显示。
  1.定义一个Mode接口

public interface IMode {
    List<String> getDatas();
}

  2.定义一个IView接口

public interface IView {

    void showProgress();

    void hideProgress();

    void showText(String text);

    void showDatas(List<String> datas);

    void showToast(String message);

}

  3.定义一个Presenter接口

public interface IPresenter {

    void getDataList();

    void onItemClick(int position);
}

  4.定义MainMode类实现IMode接口

public class MainMode implements IMode {
    @Override
    public List<String> getDatas() {
        List<String> datas = new ArrayList<>();
        //return datas;
        datas.add("A");
        datas.add("B");
        datas.add("C");
        datas.add("D");
        datas.add("E");
        datas.add("F");
        return datas;
        //return null;
    }
}

  5.定义MainPresenter类实现IPresenter接口

public class MainPresenter implements IPresenter {

    private IView mView;
    private IMode mMode;

    public MainPresenter(IView mView) {
        this.mView = mView;
        mMode = new MainMode();
    }

    @Override
    public void getDataList() {
        mView.showProgress();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                List<String> datas = mMode.getDatas();
                mView.hideProgress();
                if (datas == null) {//获取数据失败
                    mView.showText("请求数据失败!");
                } else {//获取数据成功
                    if (datas.size() == 0) { //数据为空
                        mView.showText("数据为空!");
                    } else {
                        mView.showDatas(datas);
                    }
                }
            }
        }, 2000);
    }

    @Override
    public void onItemClick(int position) {
        mView.showToast(String.format("Position %d clicked", position + 1));
    }
}

  6.定义MainActivity类实现IView接口

public class MainActivity extends AppCompatActivity implements IView, AdapterView.OnItemClickListener {

    private ListView mListView;
    private ProgressBar mProgressBar;
    private TextView mTextView;

    private IPresenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.list);
        mProgressBar = (ProgressBar) findViewById(R.id.progress);
        mTextView = (TextView) findViewById(R.id.text);
        mPresenter = new MainPresenter(this);
        mPresenter.getDataList();
    }

    @Override
    public void showProgress() {
        mProgressBar.setVisibility(View.VISIBLE);
        mListView.setVisibility(View.GONE);
    }

    @Override
    public void hideProgress() {
        mProgressBar.setVisibility(View.GONE);
        mListView.setVisibility(View.VISIBLE);
    }

    @Override
    public void showText(String text) {
        mProgressBar.setVisibility(View.GONE);
        mListView.setVisibility(View.GONE);
        mTextView.setVisibility(View.VISIBLE);
        mTextView.setText(text);
    }

    @Override
    public void showDatas(List<String> datas) {
        mListView.setAdapter(new ArrayAdapter<>(this, R.layout.simple_list_item, datas));
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        mPresenter.onItemClick(position);
    }

    @Override
    public void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}

代码地址

 

参考文章
  
  
  
  
  
  

三、使用

  使用MVP架构来实现一个ListView的显示。
  1.定义一个Mode接口

public interface IMode {
    List<String> getDatas();
}

  2.定义一个IView接口

public interface IView {

    void showProgress();

    void hideProgress();

    void showText(String text);

    void showDatas(List<String> datas);

    void showToast(String message);

}

  3.定义一个Presenter接口

public interface IPresenter {

    void getDataList();

    void onItemClick(int position);
}

  4.定义MainMode类实现IMode接口

public class MainMode implements IMode {
    @Override
    public List<String> getDatas() {
        List<String> datas = new ArrayList<>();
        //return datas;
        datas.add("A");
        datas.add("B");
        datas.add("C");
        datas.add("D");
        datas.add("E");
        datas.add("F");
        return datas;
        //return null;
    }
}

  5.定义MainPresenter类实现IPresenter接口

public class MainPresenter implements IPresenter {

    private IView mView;
    private IMode mMode;

    public MainPresenter(IView mView) {
        this.mView = mView;
        mMode = new MainMode();
    }

    @Override
    public void getDataList() {
        mView.showProgress();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                List<String> datas = mMode.getDatas();
                mView.hideProgress();
                if (datas == null) {//获取数据失败
                    mView.showText("请求数据失败!");
                } else {//获取数据成功
                    if (datas.size() == 0) { //数据为空
                        mView.showText("数据为空!");
                    } else {
                        mView.showDatas(datas);
                    }
                }
            }
        }, 2000);
    }

    @Override
    public void onItemClick(int position) {
        mView.showToast(String.format("Position %d clicked", position + 1));
    }
}

  6.定义MainActivity类实现IView接口

public class MainActivity extends AppCompatActivity implements IView, AdapterView.OnItemClickListener {

    private ListView mListView;
    private ProgressBar mProgressBar;
    private TextView mTextView;

    private IPresenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.list);
        mProgressBar = (ProgressBar) findViewById(R.id.progress);
        mTextView = (TextView) findViewById(R.id.text);
        mPresenter = new MainPresenter(this);
        mPresenter.getDataList();
    }

    @Override
    public void showProgress() {
        mProgressBar.setVisibility(View.VISIBLE);
        mListView.setVisibility(View.GONE);
    }

    @Override
    public void hideProgress() {
        mProgressBar.setVisibility(View.GONE);
        mListView.setVisibility(View.VISIBLE);
    }

    @Override
    public void showText(String text) {
        mProgressBar.setVisibility(View.GONE);
        mListView.setVisibility(View.GONE);
        mTextView.setVisibility(View.VISIBLE);
        mTextView.setText(text);
    }

    @Override
    public void showDatas(List<String> datas) {
        mListView.setAdapter(new ArrayAdapter<>(this, R.layout.simple_list_item, datas));
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        mPresenter.onItemClick(position);
    }

    @Override
    public void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}

代码地址

 

参考文章
  
  
  
  
  
  

MVP三个部分分别在干什么

  • View:对应于Activity,负责View的绘制以及用户交互
  • Model:业务逻辑和实体逻辑
  • Presenter:负责完成View与Model之间的交互

  • 定义Modle1.定义bean类:

public class People { public int icon; public String like; public String style; public int getIcon() { return icon; } public String getLike() { return like; } public String getStyle() { return style; } public void setIcon { this.icon = icon; } public void setLike(String like) { this.like = like; } public void setStyle(String style) { this.style = style; } @Override public String toString() { return "Pepole{" + "icon=" + icon + ", like='" + like + '\'' + ", style='" + style + '\'' + '}'; }

2.定义Model接口:

public interface IPeopleModel { void loadPeople(PeolpleOnLoadListener girlOnLoadListener); interface PeolpleOnLoadListener{ void onComplete(List<People> girls); }}

model处理数据逻辑:

public class PeopleModelImpl implements IPeopleModel { Handler handler = new Handler(Looper.getMainLooper; @Override public void loadPeople(final PeolpleOnLoadListener peopleOnLoadListener) { new Thread() { @Override public void run() { try { sleep; } catch (InterruptedException e) { e.printStackTrace(); } final List<People> data = new ArrayList<People>(); data.add(new People(R.drawable.f1, "五颗星", "美女1")); data.add(new People(R.drawable.f2, "四颗星", "美女2")); data.add(new People(R.drawable.f3, "五颗星", "美女3")); data.add(new People(R.drawable.f4, "三颗星", "美女4")); data.add(new People(R.drawable.f5, "五颗星", "美女5")); data.add(new People(R.drawable.f6, "三颗星", "美女6")); data.add(new People(R.drawable.f7, "四颗星", "美女7")); data.add(new People(R.drawable.f8, "五颗星", "美女8")); data.add(new People(R.drawable.f9, "四颗星", "美女9")); data.add(new People(R.drawable.f10, "三颗星", "美女10")); handler.post(new Runnable() { @Override public void run() { //回调 peopleOnLoadListener.onComplete; } }); } }.start(); }}
  • 定义Presenter从上面的图中可以看到,Presenter依赖了Model,也依赖了View,所以应该持有他们的引用。因为项目中会有很多的Presenter需要去编写,也有很多的代码会重复,所以把这些共有的东西进行封装,放到BasePresenter中。

1.编写基类Presenter

public abstract class BasePresenter<T> { protected WeakReference<T> mViewRef; public abstract void fetch(); public void attachView{ mViewRef = new WeakReference<T>; } public void detach(){ if(mViewRef!=null){ mViewRef.clear(); mViewRef = null; } }}

2.编写具体的Presenter

public class PeoplePresenterImpl extends BasePresenter<IPeopleView> { IPeopleView iGirlView; public PeoplePresenterImpl(IPeopleView iGirlView){ this.iGirlView = iGirlView; } IPeopleModel peopleModel = new PeopleModelImpl(); @Override public void fetch() { iGirlView.showloading(); if(peopleModel!=null){ peopleModel.loadPeople(new IPeopleModel.PeolpleOnLoadListener() { @Override public void onComplete(List<People> peoples) { iPeopleView.showPeople; } }); } }}
  • 定义View。

1.先确定我们的View中干什么,定义功能接口

public interface IPeopleView { void showloading(); void showPeople(List<People> girls);}

2.因为View要依赖Presenter,所以我们每个Activity肯定会持有一份Presenter的引用,所以在Activity销毁的时候要释放,这些操作放到BaseActivity中。

public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity { protected T mPresent; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPresent = createPresent(); mPresent.attachView; } @Override protected void onDestroy() { mPresent.detach(); super.onDestroy(); } public abstract T createPresent() ;}

3.编写具体的Activity

public class MainActivity extends BaseActivity<PeoplePresenterImpl> implements IPeopleView { private ListView listView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main_v1); listView =  findViewById(R.id.listview); mPresent.fetch(); } @Override public PeoplePresenterImpl createPresent() { return new PeoplePresenterImpl; } @Override public void showloading() { Toast.makeText(this,"正在拼命加载",Toast.LENGTH_SHORT).show(); } @Override public void showPeople(List<People> girls) { listView.setAdapter(new GirlListAdapter(this,girls)); }}

OK,MVP架构的练手项目就已经写完了。可以看到MVP模式中,Activity的代码会少很多。只是在展示,数据的处理和业务放在了model和presenter中。

图片 9image

现在,每个人应该都听过一个词,叫套路,没错,做任何事情都有套路,把套路搞清楚了,再复杂的东西变得简单一些,总结一下编写MVP模式的套路:

  • View:负责绘制UI元素,与用户进行交互,也就是我们的Activity。他可以从模型中读取数据,但是不能修改或更新模型。view提供提供UI交互,在presenter的控制下修改UI,将业务事件交由presenter处理,注意:
    View层不存储数据,不与Model层交互,在Android中View层一般是Activity、Fragment、View、ViewGroup等
  • Activity interface:需要View实现的接口,View通过View
    interface与Presenter进行交互,降低耦合,方便进行单元测试
  • Model:负责存储、检索、操纵数据(有时也实现一个Model
    interface用来降低耦合);表示数据模型和业务逻辑(business
    logic)。模型并不总是DataSet,DataTable之类的东西,它代表着一类组件(components)或类,这些组件或类可以向外部提供数据,同时也能从外部获取数据并将这些数据存储在某个地方。简单的理解,可以把模型想象成“外观类(facade
    class)。职责就是从网络,数据库,文件,传感器,第三方等数据源读写数据,
    对外部的数据类型进行解析转换为APP内部数据交由上层处理,对数据的临时存储,管理,协调上层数据请求。
  • Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。

  • 减少了Activity的职责,简化了Activity中的代码,将复杂的逻辑代码提取到了Presenter中进行处理。与之对应的好处就是,耦合度更低
  • Activity代码变得更加整洁,使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多。
  • 方便进行单元测试,般单元测试都是用来测试某些新加的业务逻辑有没有问题,如果采用传统的代码风格(习惯性上叫做MV模式,少了P),我们可能要先在Activity里写一段测试代码,测试完了再把测试代码删掉换成正式代码,这时如果发现业务有问题又得换回测试代码,咦,测试代码已经删掉了!好吧重新写吧……

MVP中,由于业务逻辑都在Presenter里,我们完全可以写一个PresenterTest的实现类继承Presenter的接口,现在只要在Activity里把Presenter的创建换成PresenterTest,就能进行单元测试了,测试完再换回来即可。万一发现还得进行测试,那就再换成PresenterTest吧。

接手的MVP的大项目中,有时候层层回调让我不知所云,虽然代码的耦合降低,但是让人一看,会有很懵逼的感觉,代码写太多了,没有MVC好上手。不知道大家有没有这种感觉。哈哈!!

没有最好的架构,只有最合适的架构,虽然MVP架构很好,但是在小项目中,MVC就已经很好的支持项目的开发,没必要用MVP。在大项目中可以考虑MVP的架构。搞定,收工。

源码点击下载

mvp相对于mvc优点

  • 减少了Activity的职责,简化了Activity中的代码,将复杂的逻辑代码提取到了Presenter中进行处理。与之对应的好处就是,耦合度更低。

  • Activity 代码变得更加简洁。

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

    • 一般单元测试都是用来测试某些新加的业务逻辑有没有问题,如果采用传统的代码风格(习惯性上叫做MV模式,少了P),我们可能要先在Activity里写一段测试代码,测试完了再把测试代码删掉换成正式代码,这时如果发现业务有问题又得换回测试代码,咦,测试代码已经删掉了!好吧重新写吧……

    • MVP中,由于业务逻辑都在Presenter里,我们完全可以写一个PresenterTest的实现类继承Presenter的接口,现在只要在Activity里把Presenter的创建换成PresenterTest,就能进行单元测试了,测试完再换回来即可。万一发现还得进行测试,那就再换成PresenterTest吧。

  • 避免 Activity 的内存泄露

    • 发生OOM异常的原因

      • 现内存泄露造成APP的内存不够用,而造成内存泄露的两大原因之一就是Activity泄露(Activity
        Leak)(另一个原因是Bitmap泄露(Bitmap Leak))

      • Activity是有生命周期的,用户随时可能切换Activity,当APP的内存不够用的时候,系统会回收处于后台的Activity的资源以避免OOM。

      • Java一个强大的功能就是其虚拟机的内存回收机制,这个功能使得Java用户在设计代码的时候,不用像C++用户那样考虑对象的回收问题。然而,Java用户总是喜欢随便写一大堆对象,然后幻想着虚拟机能帮他们处理好内存的回收工作。可是虚拟机在回收内存的时候,只会回收那些没有被引用的对象,被引用着的对象因为还可能会被调用,所以不能回收。

  • MVC产生内存泄漏异常分析

    • 采用传统的MVC模式,一大堆异步任务和对UI的操作都放在Activity里面,比如你可能从网络下载一张图片,在下载成功的回调里把图片加载到
      Activity 的 ImageView
      里面,所以异步任务保留着对Activity的引用。这样一来,即使Activity已经被切换到后台(onDestroy已经执行),这些异步任务仍然保留着对Activity实例的引用,所以系统就无法回收这个Activity实例了,结果就是Activity
      Leak。Android的组件中,Activity对象往往是在堆(Java
      Heap)里占最多内存的,所以系统会优先回收Activity对象,如果有Activity
      Leak,APP很容易因为内存不够而OOM。
  • MVC模式如何避免内存泄漏

    • 只要在当前的Activity的onDestroy里,分离异步任务对Activity的引用,就能避免
      Activity Leak。

MVP优点

  1. 结构分明
  2. 维护较方便
  3. View与Model解耦

MVP缺点

  1. 需要生成太多类
  2. 接口粒度不好控制

发表评论

电子邮件地址不会被公开。 必填项已用*标注