MVP(Model View Presenter)目前在Android开发中越来越重要了,MVP能够有效地降低View复杂性,避免业务逻辑被塞进View中,使得View变成一个混乱的泥坑。MVP模式会解除View与Model的耦合,同时又带来了良好的可扩展性、可测试性,保证了系统的整洁性、灵活性。
可能对于简单的应用来说MVP稍显麻烦,各种各样的接口与概念,使得整个应用充斥着零散的接口,但是对于比较复杂的应用来说,MVP模式是一种良好的架构模式,它能够非常好地组织应用结构,使得应用变得灵活,拥抱变化。
MVP模式可以分离显示层和逻辑层,它们之间通过接口进行通信,降低耦合,理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖于具体,而是依赖于抽象,这使得Presenter可以运用于任何实现了View逻辑接口的UI,使之具有更广泛的适用性,保证了灵活度。
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或者网络获取数据的角色,或者两种数据获取方式的集合。
与MVC、MVVM的区别:
1.MVC特点:
1.用户可以向View发送指令,再由View直接要求Model改变状态。
2.用户也可以直接向Controller发送指令,再由Controller发送给View。
3.Controller起到事件路由的作用,同时业务逻辑都部署在Controller中。
MVC的耦合性还是相对较高的,View可以直接访问Model,导致3者之间构成回路,因此MVP与MVC的主要区别是,MVP中的View不能直接访问Model,需要通过Presenter发出请求,View与Model不直接通信。
2.MVVM特点:
MVVM与MVP非常相似,唯一的区别是VIew和Model进行双向绑定(data-binding),两者之间有一方发生变化则会反应到另一方上。而MVP与MVVM的主要区别则是,MVP中的View更新需要通过Presenter,而MVVM则不需要,因为View与Model进行了双向绑定,数据的修改会直接反应到View角色上,而View的修改也会导致数据的变更。此时,ViewModel角色需要做的只是业务逻辑的处理,以及修改View或者Model的状态。MVVM模式有点像ListView与Adapter、数据集的关系,这个Adapter就是ViewModel角色,它与View进行了绑定,又与数据集进行了绑定,当数据集合发生变化时,调用Adapter的notifyDataSetChanged之后View就直接更新,它们之间没有直接的耦合,使得ListView变得更为灵活。
注意:
由于Presenter经常性的需要执行一些耗时操作,例如,请求网络数据,Presenter持有了Activity的强引用,如果在请求结束之前Activity被销毁了,那么由于网络请求还没有返回,导致Presenter一直持有Activity对象,使得Activity对象无法被回收,此时就发生了内存泄露。如何来解决这样的问题?
可以通过弱引用和Activity、Fragment的生命周期来解决这个问题,首先建立一个Presenter抽象,它是一个泛型类,泛型类型为View角色要实现的接口类型:
public abstract class BasePresenter<T> {
protected Reference<T> mViewRef;//View接口类型的弱引用
public void attachView(T view){
mViewRef = new WeakReference<T>(view); //建立关联
}
protected T getView(){
return mViewRef.get();
}
public boolean isViewAttached(){
return mViewRef != null && mViewRef.get() != null;
}
public void detachView(){
if(mViewRef != null){
mViewRef.clear();
mViewRef = null;
}
}
}
BasePresenter有4个方法,分别与View建立关联、解除关联、判断是否与View建立了关联、获取View。View类型通过BasePresenter的泛型类型传递进来,Presenter对这个View持有弱引用。通常情况下这个View类型应该是实现了某个特定接口的Activity或者Fragment等类型。
创建一个MVPBaseActivity基类,通过这个基类的生命周期函数来控制它与Presenter的关系;
public abstract class MVPBaseActivity<V,T extends BasePresenter<V>> extends Activity{
protected T mPresenter; // Presenter对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter();
mPresenter.attachView((V)this); //View 与Presenter建立关联
}
@Override
protected void onDestroy() {
super.onDestroy();
mPresenter.detachView();
}
protected abstract T createPresenter();
}
MVPBaseActivity含有两个泛型参数,第一个是View接口类型,第二个是Presenter的具体类型,通过泛型参数,使得一些通用的逻辑可以被抽象到MVPBaseActivity类中。例如,在MVPBaseActivity的onCreate函数中,会通过createPresenter函数创建一个具体的Presenter,这个Presenter的类型就是BasePresenter<T>类型。构建Presenter之后调用attachView函数与Activity建立关联。而在onDestory函数中,则会与Activity解除关联,从而避免内存泄漏。有的人可能会有疑问,如果在onDestory中解除了对Activity的引用,那么就没有必要再用弱引用了。并不是在任何情况下Activity的onDestory都会被调用,一旦这种情况发生,弱引用也能够保证不会造成泄漏。而通过MVPBaseActivity的封装维护Presenter与View关联关系的代码,使得子类可以避免重复的代码。如:
public class DemoActivity extends MVPBaseActivity<ArticleViewInterface, ArticlePresenter> implements ArticleViewInterface{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initViews();
mPresenter.fetchArticles();
}
private void initViews() {
}
@Override
protected ArticlePresenter createPresenter() {
return null;
}
@Override
public void showArticles(List<String> data) {
// TODO Auto-generated method stub
}
@Override
public void showLoading() {
// TODO Auto-generated method stub
}
@Override
public void hideLoading() {
// TODO Auto-generated method stub
}
}
此时,Presenter的创建以及与View建立关联等操作都被封装到MVPBaseActivity ,消除了子类重复代码的同时又避免了Activity的内存泄漏问题,你可以为Fragment、FragmentActivity等类型都建立一个类似这样的基类,这样一来你就可以将这种思想运用到更广阔的范围。
MVP是开发过程中非常值推荐的架构模式,它能够将各组件进行解耦,并且带来良好的可扩展性,可测试性,稳定性、可维护,同时使得每个类型的职责相对单一、简单,避免了大量的“臃肿”的程序,例如数千行的Activity类,它有效的将业务逻辑、数据处理等工作从Activity等View元素中抽离出来,使得每个类尽可能简单,同时每个模块能够独立进行演化。它的思想也非常好地体现了面向对象的设计原则,即抽象、单一职责、最小化、低耦合。