参考:
在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,并接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。当我们将其中复杂的逻辑处理移至另外的一个类(Presneter)中时,Activity其实就是MVP模式中View,它负责UI元素的初始化,建立UI元素与Presenter的关联(Listener之类),同时自己也会处理一些简单的逻辑(复杂的逻辑交由Presenter处理).
MVP模式可以让显示界面和数据分离,我们开发的应用可以分离至少三层,这样也可以进行独立测试。有了MVP我们就可以从Activity中分离大部分代码,而且不用单元测试可以对每个模块进行单独测试了。
MVP 即 Model – Presenter – View,各部分之间的通讯,都是双向的,Presenter 持有 View 和 Model 的抽象引用,作为中间人,处理业务逻辑,Model 角色用于调取数据,而 View 则用于展现和控制 UI,它们都由中间人 P 调度。抽象的好处前面说了,如果 M 或 V 有改变,只要换一个实现者就好了,对于 P,可以继续把它们当成提供一样的可调用方法对象。
对于包的结构,如果项目比较小,可以把不同的 Presenter 置于同一个 package 下,而如果页面数很多项目模块很多,则可以每一个模块分一套 MVP packages。
现在我们只要在 Activity 或 Fragment 中的生命周期简单做一些 UI 组件初始化等布置,然后一些业务逻辑工作就请求 Presenter 去完成,Presenter 内部持有 View 和 Model,Activity 是 View(MVP 中的 View) 的实现,于是 Presenter 调用 Model 去请求得到数据库或网络数据,完成之后再调用 View 改变 UI 的方法,或者把数据交给 View 去展现。如此,每个角色的代码都会变得很简洁、明确。而且,将业务逻辑放到 Presenter 中去,可以避免当 Activity 退出而后台线程仍然引用着 Activity,致使资源无法被系统回收从而引起内存泄露。
对于屏幕旋转 Activity 重建和内存泄漏等问题,MVP 能够更好地处理。当 Activity 销毁的之前,可以把 Presenter 中的引用去除,因此不会在切换屏幕的时候发生内存泄漏,而且没必要去 unsubscribe 请求。具体推荐大家看看这篇文章:使用 MVP 和后台任务。
在MVP模式里通常包含4个要素:
(1)View:负责绘制UI元素、与用户进行交互(在Android中体现为Activity);
(3)Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合);
(4)Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。
MVP模式:
· View不直接与Model交互,而是通过与Presenter交互来与Model间接交互
· Presenter与View的交互是通过接口来进行的,更有利于添加单元测试
· 通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑
MVC模式:
· View可以与Model直接交互
· Controller是基于行为的,并且可以被多个View共享
· 可以负责决定显示哪个View
回想一下你在开发Android应用时是如何对代码逻辑进行单元测试的?是否每次都要将应用部署到Android模拟器或真机上,然后通过模拟用户操作进行测试?然而由于Android平台的特性,每次部署都耗费了大量的时间,这直接导致开发效率的降低。而在MVP模式中,处理复杂逻辑的Presenter是通过interface与View(Activity)进行交互的,这说明我们可以通过自定义类实现这个interface来模拟Activity的行为对Presenter进行单元测试,省去了大量的部署及测试的时间。
MVC → MVP
当我们将Activity复杂的逻辑处理移至另外的一个类(Presenter)中时,Activity其实就是MVP模式中的View,它负责UI元素的初始化,建立UI元素与Presenter的关联(Listener之类),同时自己也会处理一些简单的逻辑(复杂的逻辑交由 Presenter处理)。
MVP的Presenter是框架的控制者,承担了大量的逻辑操作,而MVC的Controller更多时候承担一种转发的作用。因此在App中引入MVP的原因,是为了将此前在Activty中包含的大量逻辑操作放到控制层中,避免Activity的臃肿。
两种模式的主要区别:
· (最主要区别)View与Model并不直接交互,而是通过与Presenter交互来与Model间接交互。而在MVC中View可以与Model直接交互
· 通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑。而Controller是基于行为的,并且可以被多个View共享,Controller可以负责决定显示哪个View
· Presenter与View的交互是通过接口来进行的,更有利于添加单元测试。
因此我们可以发现MVP的优点如下:
1、模型与视图完全分离,我们可以修改视图而不影响模型;
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部;
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁;
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)。
具体到Android App中,一般可以将App根据程序的结构进行纵向划分,根据MVP可以将App分别为模型层(M),UI层(V)和逻辑层(P)。
UI层一般包括Activity,Fragment,Adapter等直接和UI相关的类,UI层的Activity在启动之后实例化相应的Presenter,App的控制权后移,由UI转移到Presenter,两者之间的通信通过BroadCast、Handler或者接口完成,只传递事件和结果。
举个简单的例子,UI层通知逻辑层(Presenter)用户点击了一个Button,逻辑层(Presenter)自己决定应该用什么行为进行响应,该找哪个模型(Model)去做这件事,最后逻辑层(Presenter)将完成的结果更新到UI层。
MVP模式的核心思想:
MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。
这就是MVP模式,现在这样的话,Activity的工作的简单了,只用来响应生命周期,其他工作都丢到Presenter中去完成。从上图可以看出,Presenter是Model和View之间的桥梁,为了让结构变得更加简单,View并不能直接对Model进行操作,这也是MVP与MVC最大的不同之处。
MVP模式的作用
·分离了视图逻辑和业务逻辑,降低了耦合
·Activity只处理生命周期的任务,代码变得更加简洁
·视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性
·Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试
·把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存泄露和OOM
其中最重要的有三点:
Activity 代码变得更加简洁
相信很多人阅读代码的时候,都是从Activity开始的,对着一个1000+行代码的Activity,看了都觉得难受。
使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多。
方便进行单元测试
一般单元测试都是用来测试某些新加的业务逻辑有没有问题,如果采用传统的代码风格(习惯性上叫做MV模式,少了P),我们可能要先在Activity里写一段测试代码,测试完了再把测试代码删掉换成正式代码,这时如果发现业务有问题又得换回测试代码,咦,测试代码已经删掉了!好吧重新写吧……
MVP中,由于业务逻辑都在Presenter里,我们完全可以写一个PresenterTest的实现类继承Presenter的接口,现在只要在Activity里把Presenter的创建换成PresenterTest,就能进行单元测试了,测试完再换回来即可。万一发现还得进行测试,那就再换成PresenterTest吧。
避免 Activity 的内存泄露
Android APP 发生OOM的最大原因就是出现内存泄露造成APP的内存不够用,而造成内存泄露的两大原因之一就是Activity泄露(Activity Leak)(另一个原因是Bitmap泄露(Bitmap Leak))。
Java一个强大的功能就是其虚拟机的内存回收机制,这个功能使得Java用户在设计代码的时候,不用像C++用户那样考虑对象的回收问题。然而,Java用户总是喜欢随便写一大堆对象,然后幻想着虚拟机能帮他们处理好内存的回收工作。可是虚拟机在回收内存的时候,只会回收那些没有被引用的对象,被引用着的对象因为还可能会被调用,所以不能回收。
Activity是有生命周期的,用户随时可能切换Activity,当APP的内存不够用的时候,系统会回收处于后台的Activity的资源以避免OOM。
采用传统的MV模式,一大堆异步任务和对UI的操作都放在Activity里面,比如你可能从网络下载一张图片,在下载成功的回调里把图片加载到 Activity 的 ImageView 里面,所以异步任务保留着对Activity的引用。这样一来,即使Activity已经被切换到后台(onDestroy已经执行),这些异步任务仍然保留着对Activity实例的引用,所以系统就无法回收这个Activity实例了,结果就是Activity Leak。Android的组件中,Activity对象往往是在堆(Java Heap)里占最多内存的,所以系统会优先回收Activity对象,如果有Activity Leak,APP很容易因为内存不够而OOM。
采用MVP模式,只要在当前的Activity的onDestroy里,分离异步任务对Activity的引用,就能避免 Activity Leak。