细谈MVP架构模式

什么是MVP架构?:

MVP就是Model-View-Presenter,MVP是从经典的模式MVC演变而来,它们的基本思想有相通的地方:

Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个

重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,

所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。在MVC里,

View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。在MVC模型里,

更关注的Model的不变,而同时有多个对Model的不同显示,及View。所以,在MVC模型里,Model不依赖于View,

但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,

至少那些业务逻辑是无法重用的。用流程图的方式解释就更清楚了:



MVP和MVC的区别,及MVP是如何解决MVC的问题?

MVP架构:

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

Model: 依然是业务逻辑和实体模型

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


View不直接与Model交互,而是通过与Presenter交互来与Model间接交互。

Presenter与View的交互是通过接口来进行的。

通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑。


MVC架构:

View:对应于布局文件

Model:业务逻辑和实体模型

Controllor:对应于Activity

View可以与Model直接交互。

Controller是基于行为的,并且可以被多个View共享。

可以负责决定显示哪个View。

总结解释一下就是说:

从MVC到MVP的一个转变,就是减少了Activity的职责,减轻了它的负担,简化了Activity中的代码和一些操作,

将逻辑代码提取到了Presenter中进行处理,降低了其耦合度。

MVP的优点

1.降低耦合度,隐藏数据,Activity中代码更简洁

2.模块职责划分明显3.方便测试驱动开发4.代码复用度较高5.代码灵活性


基于MVP架构编写代码:

我们来先看下目录层:这里用展示一个列表数据为例。

1.View层:

MainActivity.java:

public class MainActivity extends AppCompatActivity implements MvpView {
    private ListView listview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listview = (ListView) findViewById(R.id.lv);
        //需要通过某种方式获取数据源
        new MainAcPresenter(this).setModel(1).load();
    }

    @Override
    public void showData(List<String> list) {
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
        listview.setAdapter(arrayAdapter);
    }
}

MvpView.java
界面接口,这个接口封装的方法基本上都跟视图展示有关。

public interface MvpView {
    public void showData(List<String> list);//更新界面得有数据源
}


2.model层:

MvpModel.java
封装的方法基本上是跟取数据相关的操作

public interface MvpModel {
    void getData(onLoadCompleteListener onLoadCompleteListener);

    interface  onLoadCompleteListener{
        void onLoadComplete(List<String> list);
    }
}

MainModel2.java:

	
public class MainModel2 implements MvpModel {
    @Override
    public void getData(onLoadCompleteListener onLoadCompleteListener) {
        //这里方式变了
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add("这是更新后的数据"+i+"条");
        }
        onLoadCompleteListener.onLoadComplete(list);
    }
}

MainAcModel.java:

public class MainAcModel implements MvpModel {
    @Override
    public void getData(onLoadCompleteListener onLoadCompleteListener) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add("这是第" + i + "条数据");
        }
        if(onLoadCompleteListener !=null){
            onLoadCompleteListener.onLoadComplete(list);
        }
    }
}

这里写了两个model是为了演示一个界面中取数据的两种不同方式。


3.presenter层(核心):

MainAcPresenter.java:
Presenter是Model和View之间交互的桥梁,里面有一些业务逻辑的操作。

public class MainAcPresenter {
    private MvpView mvpView;
    private MvpModel mvpModel;

    public MainAcPresenter(MvpView mvpView) {
        this.mvpView = mvpView;
        this.mvpModel = new MainAcModel();
    }
    public MainAcPresenter setModel(int model) {
        switch (model) {
            case 0:
                mvpModel = new MainAcModel();
                break;
            case 1:
                mvpModel = new MainModel2();
                break;

        }
        return this;
    }


    public void load() {
        mvpModel.getData(new MvpModel.onLoadCompleteListener() {
            @Override
            public void onLoadComplete(List<String> list) {
			//调用MainActivity中的showData()方法,并将list集合传到activity中。
                mvpView.showData(list);	
            }
        });
    }
}

上面代码的整体思路就是:将页面的显示与数据的获取解耦,在没用mvp模式之前,都是将数据获取和界面逻辑全部

写在activity中,这样导致了代码结构不清晰,不容易去维护。MVP的核心就是将数据的获取与界面逻辑分离,通过第

三者Presenter去讲两者关联起来,进而相应的业务逻辑(数据+界面显示)集中于presenter层中,如果一个项目由三个

做,就可以按此模块分工开发,写界面的去关注界面模块,进行数据操作的就只关注数据操作模块,写业务逻辑的

也可以专注于业务逻辑,这样子分工就很明确,并且也不会互相影响。


但是同时上面的代码依然存在着问题,还不能用于实际开发中。
因为Presenter经常性地需要执行一些耗时操作,例如请求网络数据。而presenter持有了MainActivity的强引用,如果

在请求结束之前Activity被销毁了,那么由于网络请求还没有返回,导致presenter一直持有MainActivity对象,使得

MainActivity对象无法被回收,此时就发生了内存泄漏。


那我们该如何解决这样的问题呢?
我们的答案是,通过弱引用和Activity或者fragment的声明周期来解决这个问题。首先建立一个presenter抽象,我们命

名为BasePresenter,它是一个泛型类。

public class BasePresenter<V extends MvpView> {
    private WeakReference<V> weakReference;			//view接口类型的弱引用

    public void attach(V mvpView) {
        weakReference = new WeakReference(mvpView);	//建立关联
    }

    public void deAttch() {
        if (weakReference != null) {
            weakReference.clear();
            weakReference = null;
        }
    }

    public V getView() {
        return weakReference.get();
    }
	
	public boolean isViewAttached(){
		return weakReference!=null && weakReference.get()!=null;
	}
}
BasePresenter有4个方法,分别与view建立关联、解除关联、判读是否与view建立了关联、获取view.View类型通过

BasePresenter的泛型类型传递过来,Presenter对这个View持有弱引用,通常情况下这个View类型应该是实现了某个

特定接口的Activity或者Fragment等类型。

MainAcPresenter修改如下:

public class MainAcPresenter extends BasePresenter<MvpView> {
    private MvpModel mvpModel;

    public MainAcPresenter() {
        mvpModel = new MainAcModel();
    }
    public MainAcPresenter setModel(int model) {
        switch (model) {
            case 0:
                mvpModel = new MainAcModel();
                break;
            case 1:
                mvpModel = new MainModel2();
                break;

        }
        return this;
    }


    public void load() {
        mvpModel.getData(new MvpModel.onLoadCompleteListener() {
            @Override
            public void onLoadComplete(List<String> list) {
                getView().showData(list);
            }
        });
    }
}


创建一个BaseActivity基类,通过这个基类的声明周期函数来控制它与presenter的关系。

public abstract class BaseActivity<V extends MvpView,T extends BasePresenter<V>> 
	extends AppCompatActivity{

    public T basePresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        basePresenter = getBasePresenter();
        basePresenter.attach((V) this);//最终的this代表具体的子类,子类在继承的时候就必须实现MvpView
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        basePresenter.deAttch();
    }

    public abstract T getBasePresenter();
}

BaseActivity含有两个泛型参数,第一个是View接口类型,第二个是presenter的具体类型。通过泛型参数,使得一些

通用的逻辑可以被抽象到BaseActivity类中。例如,在BaseActivity的onCreate函数中,会通过getBasePresenter()函数

创建一个具体的presenter,这个presenter的类型就是BasePresenter<T>类型。构建Presenter之后调用attachView函

数与Activity建立关联。而在onDestory函数中,则会与Activity解除关联,从而避免内存泄漏。有人会问,如果在

onDestory中解除了对Activity的引用,那么久没有必要再用弱引用了,但是并不是任何情况下Activity的onDestroy方法

都会被调用,一旦这种情况发生了,弱引用也能够保证不会造成内存泄漏。


view层中的MainActivity修改如下:

public class MainActivity extends BaseActivity<MvpView,MainAcPresenter> 
	implements MvpView{

    private ListView listview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listview = (ListView) findViewById(R.id.lv);
        //需要通过某种方式获取 数据源
        basePresenter.setModel(1).load();
    }

    @Override
    public MainAcPresenter getBasePresenter() {
        return new MainAcPresenter();
    }


    @Override
    public void showData(List<String> list) {
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,list);
        listview.setAdapter(arrayAdapter);
    }
}

此时,presenter的创建以及与view建立关联等操作都被封装到BaseActivity中,消除了子类重复代码的同时又避免了

Activity的内存泄漏问题。可以为Fragment、FragmentActivity等类型都建立一个类似这样的基类。


大总结:


从整体效果来看,MVP是开发过程中非常值得推荐的架构模式,它能够将各组件进行解耦,并且带来良好的可扩

展性、可测试性、稳定性、可维护性,同时使得每个类型的职责相对单一、简单,避免了“臃肿”程序的存在。它的思

想也非常好的提现了面向对象的设计原则,即抽象、单一职责、最小化、低耦合。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值