Android开发中的MVP模式详解

[复制链接]
单车手 发表于 2019-5-15 14:54:20 | 显示全部楼层 |阅读模式
来源:https://www.jianshu.com/p/479aca31d993
若内容不全,可点击上述链接查看来源网页,在网页中点击红色双层向下的箭头阅读全文


在Android开发中,我们通常会去将项目分成一个个的模块文件夹,来进行管理维护,有的人是直接按照功能来分模块,这也是最常见的,有的人则会按照一定的设计模式,再结合功能来进行项目模式设计,比如MVP、MVVM这两种目前比较流行的项目设计模式,本文主要讲解MVP模式。

MVC、MVP、MVVMMVC

对于MVC我想大家应该都不陌生,最典型的MVC就是JSP+servlet+javabean的模式了。

MVC1MVC2

这两张是我从百度中截来的图(原谅我太懒,不想画图),从中很容易看出MVC的大致流程,用户通过操作View来发送用户请求;Controller接收到用户请求后,负责决定应该调用哪个Model来进行处理;然后Model根据用户请求进行相应的业务逻辑处理,并返回数据;最后Controller调用相应的视图View来显示Model返回的数据。

MVPMVP

首先,我们来看一下上图(没错,又是从百度上抠下来的?_?),View发送指令给Presenter,Presenter获取指令后,调用响应的Model进行业务逻辑处理,然后返回数据给Presenter,Presenter根据Model返回的数据再来调用相应的View。

MVVMMVVM

看上图(盗图狂魔就是我@_@),在MVVM中有个ViewModel,它的作用就是与View进行双向绑定,当View或者ViewModel有一方变动时,另一方也会跟着改变,其实就是观察者模式,同时ViewModel也会处理一些轻量的业务逻辑,具有和MVP中的Presenter的一些类似功能。当用户对View进行操作时,ViewModel就会直接收到指令,然后调用Model处理业务逻辑,当Model返回数据给ViewModel时,因为ViewModel与View双向绑定的缘故,ViewModel接收到数据后,View也会跟着改变,省去了ViewModel特意调用View来改变View的状态这一步骤!有兴趣的同学,可以去体验一下AndroidStudio中的databinding简单感受一下。

为什么用MVP

在Android中,对于Activity并没有明确的说它是属于View还是Controller的范畴,Activity既有View的性质,也具有Controller的性质,所以导致MVC在Android中很难明确分工使用,导致Activity很重。而且MVC中View会与Model直接交互,所以Activity与Model的耦合性很高,当后期维护时,稍有变动,可能Model、Activity、XML都会跟着改变,工作量很大,成本太高。

而MVP与MVC最大的不同之处是,MVP将M与V分隔开来,通过P交互,这样在Android中,就可以明确的把Activity当作View处理,虽然可能还有一点逻辑在其中,但是已经无伤大雅;View和Model不直接交互,当View有变动或者Model有变动时,不会相互影响,有太大变动,,耦合性低,对于后期维护来说,特别是项目越来越庞大时,可以很快的理清项目结构,找到需要修改的地方,大大的缩短了工作量。而且,因为View与Model分离的缘故,Model可以单独进行单元测试。

对于MVVM,其实很多框架中都使用到了它,比如说Vue.js、AngularJs都使用到了这种模式,在Android中也有DataBinding这个原生插件可以使用,来达到双向绑定的作用,但只是使用了DataBinding并不是完全的MVVM模式,个人认为还必须有个中间层类似与Presenter一样的层,来处理一些交互;说实话,在Android中使用了MVVM后,的确大大的提高了开发效率,省去了很多代码,但是如果只是使用纯粹的MVVM,当项目变得庞大后,还是有些吃不消的,各种改动的工作量不是闹着玩的,所以个人认为MVVM只是适合中小型项目,对于大项目还是有点吃紧的。

所以,最后个人认为如果你的项目会越来越庞大,但是又想体验MVVM那种便利,不妨试试MVP+DataBinding,其实这就有点类似于MVPVM模式了,方便快捷,即使项目庞大,改动时也不需要太多重构。

MVPLoader

好了,上面说了那么多,我们还是来点实际的吧,下面是本人在项目中对MVP的处理方式,有不同见解的,欢迎大家提出。

==================================View=======================================所有的view(Activity、FragmentActivity、Fragment...)都必须实现这个接口publicinterfaceIView{//此方法是为了当Presenter中需要获取上下文对象时,传递上下文对象,而不是让Presenter直接持有上下文对象ActivitygetSelfActivity();}这是Activity的基类:publicabstractclassBaseActivity<PextendsIPresenter>extendsActivityimplementsIView{//Presenter对象protectedPMvpPre;@OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState){super.onCreate(savedInstanceState);MvpPre=bindPresenter();}//绑定PresenterprotectedabstractPbindPresenter();public<T>T$(intresId){return(T)findViewById(resId);}public<T>T$(intresId,Viewparent){return(T)parent.findViewById(resId);}@OverridepublicActivitygetSelfActivity(){returnthis;}@OverrideprotectedvoidonDestroy(){super.onDestroy();/***在生命周期结束时,将presenter与view之间的联系断开,防止出现内存泄露*/if(MvpPre!=null){MvpPre.detachView();}}}==================================Presenter=======================================publicinterfaceIPresenter{voiddetachView();}Presenter的基类:publicabstractclassBasePresenter<VextendsIView>implementsIPresenter{//此处使用弱引用是因为,有时Activity关闭不一定会走onDestroy,所以这时使用弱引用可以及时回收IViewprotectedReference<V>MvpRef;publicBasePresenter(Vview){attachView(view);}privatevoidattachView(Vview){MvpRef=newWeakReference<V>(view);}protectedVgetView(){if(MvpRef!=null){returnMvpRef.get();}returnnull;}/***主要用于判断IView的生命周期是否结束,防止出现内存泄露状况**@return*/protectedbooleanisViewAttach(){returnMvpRef!=null&&MvpRef.get()!=null;}/***Activity生命周期结束时,Presenter也清除IView对象,不在持有*/@OverridepublicvoiddetachView(){if(MvpRef!=null){MvpRef.clear();MvpRef=null;}}}===================================demo========================================接口:/***创建一个类作为纽带,将view、presenter、model的接口方法都串联在一起,更加便于管理*/publicfinalclassMainContacts{publicinterfaceIMainextendsIView{voidshowTips(booleanisSucceess);}publicinterfaceIMainPreextendsIPresenter{voidlogin(Stringusername,Stringpassword);}publicinterfaceIMainLgc{booleanlogin(Stringusername,Stringpassword);}}Model部分:publicclassMainLogicimplementsMainContacts.IMainLgc{publicbooleanlogin(Stringusername,Stringpassword){if(TextUtils.isEmpty(username)||TextUtils.isEmpty(password)){returnfalse;}returntrue;}}View部分:publicclassMainActivityextendsBaseActivity<MainPresnter>implementsMainContacts.IMain{privateEditTexteditT_username,editT_password;privateButtonbtn_login;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initUI();addListeners();}@OverrideprotectedMainPresnterbindPresenter(){returnnewMainPresnter(this);}privatevoidinitUI(){editT_username=$(R.id.editT_username);editT_password=$(R.id.editT_password);btn_login=$(R.id.btn_login);}privatevoidaddListeners(){btn_login.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){MvpPre.login(editT_username.getText().toString(),editT_password.getText().toString());}});}@OverridepublicvoidshowTips(booleanisSucceess){if(isSucceess){Toast.makeText(this,"登录成功!",Toast.LENGTH_SHORT).show();}else{Toast.makeText(this,"登录失败!",Toast.LENGTH_SHORT).show();}}}Presenter部分:publicclassMainPresnterextendsBasePresenter<MainContacts.IMain>implementsMainContacts.IMainPre{privateMainLogicmMainLogic;publicMainPresnter(MainContacts.IMainview){super(view);this.mMainLogic=newMainLogic();}@Overridepublicvoidlogin(Stringusername,Stringpassword){//判断activity的生命周期是否结束,不判断的话在极端情况下可能会出现内存泄露if(isViewAttach()){MvpRef.get().showTips(mMainLogic.login(username,password));}}}

好了,以上便是本人对MVP模式的一些理解,如果你不想自己在重新搭建MVP框架,可以直接使用MVPLoader项目做依赖:

Step1:allprojects{repositories{...maven{url'https://jitpack.io'}}}Step2:dependencies{compile'com.github.albert-lii:MVPLoader:1.0.6'}

如果你有不同的理解,也欢迎提出,大家一起进步。

Github地址https://github.com/albert-lii/MVPLoader

如果觉得不错,给个赞吧