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