MVC
Model:负责数据的处理,如数据库操作、网络数据请求等
View:负责界面的展示,主要是xml布局文件
Controller:负责把Model层处理的数据展示到View层,主要是Activity、Fragment
在Android中的应用主要是通过Controller来操作Model层的数据,并通过UI绑定把数据处理结果展示到View层。
优点:MCV很好地实现了业务模块的划分,降低了项目的耦合度。
缺点:Android中的MVC与传统的MVC不一样,Android中的Controller是Activity,并不是真正的Controller,它还承担着部分View的功能,如界面初始化操作以及接受用户点击事件,同时还要进行UI数据绑定以及事件监听处理。所以当项目变大的时候,会导致Activity变得臃肿,难以维护。同时View层和Model相互耦合,不易于开发和维护。
MVP
Model:负责数据的处理,如数据库操作、网络数据请求等
View:负责界面的展示,主要是Activity、Fragment
Presenter:负责完成Model层与View层的交互
优点 :
(1)View层和Model层完全解耦,大大降低了项目的耦合度;
(2)由于三者之间依赖于抽象,不依赖与具体,所以易于扩展,灵活性高;
(3)能解决UI易变性的问题,即实现同一份逻辑代码搭配不同的显示界面 ;
(4) 易于测试;
以实现一个简单的登录验证为例子:
MVP的项目结构如下:

定义View的抽象接口,该接口主要描述了登录操作开始到登录结束这一过程中需要对界面进行的渲染操作,并把这些操作抽象出来:

定义Model的抽象接口,该接口主要描述了登录验证需要进行的耗时操作:

定义Presenter的抽象接口,该接口主要描述了登录验证的逻辑操作:

接下来介绍整个登录验证的完整逻辑:
首先LoginActivity实现了ILoginView接口,并实现了该接口中的方法,该接口的方法回调时机取决于Presenter层的实现,由于把LoginActivity本身作为参数在创建Presenter时设置进去,所以这就建立了View层和Presenter层的联系,同时View层会持有Presenter的引用,可以把用户输入的数据通过传递给Presenter层进行处理。


LoginPresenterImpl实现了ILoginPresenter接口,并重写该接口主要的方法,在该方法中实现主要的登录验证逻辑。Presenter层持有View层和Model层的引用,Presenter层的登录验证逻辑主要通过调用Model层login()处理方法,同时通过OnLoginFinishedListener接口来充当Presenter层和Model层的通信,把Model层的操作结果通过接口回调的形式返回到Presenter层,用于设置View层的UI渲染接口的回调,此时 建立了Model层和Presenter层的联系。


LoginModelImpl实现了ILoginModel接口,主要执行2s的耗时操作来模拟登陆验证的功能,并通过OnLoginFinishedListener接口把操作结果的数据回传给Presenter层,以便对View层进行数据渲染。所以从这里可以看出Model层并没有直接操作View层,而是通过Presenter层这个中介来建立View和Model之间的交互,从而解耦了View和Model,这正是MVP和MVC的本质区别所在。

MVP内存泄漏解决办法
由于View和Presenter建立联系时,Presenter会持有Activity的强引用,而且Presenter层会经常执行一些耗时操作,这时候如果在耗时操作结束前Activity被销毁了,因为Presenter的耗时操作尚未结束,会导致Presenter一直持有Activity的引用,Activity对象无法被回收,从而发生内存泄漏。
解决办法是Presenter绑定View的生命周期,并且使用弱引用声明View。也就是把Presenter层持有的Activity声明为弱引用,而在View层的onCreate()方法中创建Presenter建立关联,同时在onDestroy()方法中解除关联。这样当Activity销毁时会回调onDestroy()方法解除与Presenter的关联,同时如果Activity因某种原因无法回调onDestroy()方法时,弱引用也能保证不会出现内存泄漏。
总结
通过上面的分析可以看出,Presenter对View是完全解耦合的,因为Presenter持有的是ILoginView这个抽象接口,而不是具体类LoginActivity,这时候如果UI界面发生变化时,也就是LoginActivity需要被更换为其它Activity时,只需让更换后的Activity实现ILoginView接口以及相关逻辑就可以重用登录验证的业务逻辑, 即实现同一份逻辑代码搭配不同的显示界面 。而且由于Presenter抽取了业务逻辑,使得Activity只需要完成View的初始化操作就行,易于维护。
同样的,Presenter对Model也是解耦合的,因为Presenter持有的是ILoginModel抽象接口,这时候如果需要替换数据库引擎的话,只需要重新构建一个实现ILoginModel接口的类并实现相关的存取逻辑即可。
参考
《Android进阶之光》 第10章 应用架构设计
《Android源码设计模式解析与实战》 第25~27章 MVC、MVP、MVVM应用架构模式
网友评论