聊聊Google官方MVP
MVP这个话题也是讨论了很久很久的,热度一直不减,甚至google官方也很认可MVP在Android中的地位。
而我最近一个项目也参照google的那个MVP架构,发现并怎么好用。
果然架构这种东西需要自己思考,根据不同的项目进行变更,MVP MVC MVVM总的来说就是一个架构模式,具体应该怎么实现还是需要依赖于自己不断的探索,照搬别人的项目的话是不可能适合你的项目的。所以网上的MVP项目也是五花八门,看得我欲仙欲死。
google工程师也说了,他们放出的那个Android Architecture只是作为参考,具体怎么实现mvp架构取决于你。
而我也在这篇文章中聊一下我这一个月来照搬Google MVP的感受。
可以先看看这篇文章简单的了解一下google官方mvp
Android官方MVP架构示例项目解析
一、 V层:Fragment
google 用Fragment这个控件作为MVP中的View层,什么回事?
Fragment作为View,那么在Activity就可以初始化Presenter,View和其他一些依赖,让依赖注入独立于MVP外,Activity看起来就像一个Injector。如果Activity作为View的话,那么你需要在Activity中初始化一大堆依赖,View就不是单纯的View了。
这虽然看起来很符合MVP的架构思想,进一步减少了View的工作。但实际上,不好的地方也是很多的。
写项目的时候,不仅要多敲一个Fragment,而且在Activity中也是做反复性的工作:new Fragment,addFragment, new Present……
而且Fragment用起来并没有Activity那么便利,还存在各种莫名其妙的bug,最致命的一点就是:
我们完全可以使用Dagger2进行依赖注入,那么Fragment作为View的好处几乎就没有了。
在CleanArchitecture这个开源项目中,虽然也是用Fragment作为View层,但好歹别人用setRetainInstance()维持Presenter啊(presenter生命周期,下面会讲到),不过这方法用不好的话很容易导致内存泄漏……
所以,最终我还是会选择Activity作为View,除非某些页面确实需要用到Fragment。
关于Fragment可以看看这篇文章:我为什么不主张使用Fragment
二、Contract协议类
google用一个Contract类定义了View层接口和Presenter层接口,这看起来是很不错的设计,看起来很符合依赖倒置原则。
但是Presenter真的有必要实现接口吗?
在MVP模式中,Presenter是高层次模块,而View是低层次模块。
而使用依赖倒置原则主要是为了高层和低层模块的解耦,方便替换低层次模块的时候不会影响到高层模块,所以在MVP模式中,View需要实现接口,你可以随时将View替换成Activity,Fragment或者其他,并不会印象到Presesnter。
View使用接口还有另外一个好处:方便测试。你不可能在对Presenter进行单元测试的时候去Mock一个xxxActivity对吧……
而View虽然也依赖于Presenter,但是View和Presenter是一一对应的,Presenter只会有一个,并不可能出现其他实现,所以Presenter实现接口完全是多余的,不仅多了一个无意义的类,还累着自己。
并且,在View中想看Presenter代码的时候,ctrl+左键点击Presenter提示一大堆同名的Presenter。选择其中一个点进去之后,跳转到的是contract类,最终结果你还是需要到目录去找……
关于Presenter到底该不该使用接口解耦的一篇文章,我个人是非常认同作者的。
不要再给MVP中Presenter写接口了
三、P层的生命周期
这一块也是MVP架构中的一个难点
google的mvp很明显没有做额外的Presenter的生命周期管理,让它随着View的生命周期结束而结束。
这看起来没什么问题,但是你们想想,Presenter往往会保存View的一些状态,比如保存是否已经请求过数据的变量,或者某些请求参数。
当Activity或者Fragment因为某些原因生命周期重走了(比如旋转屏幕或者被系统回收),你的Presenter也会重新实例化一个。
如果用户没事做一直来回的旋转屏幕,那么你的Presenter就一次次的实例化,一次次的请求网络……最终可能导致内存溢出或者ANR
不过我们国内的app一般都禁止横屏,所以这一点也不是很重要,但始终算是一个不好的地方吧。
对于延长Activity生命周期的方法有好几种方式
可以看看这篇文章通过Loader延长Presenter生命周期
之前说的使用Fragment的setRetainInstance也是其中一种方法
四、Model层(Repository层)
在上面的图中,也可以看到Google的Model层使用了多种数据源。
内存,本地,网络。
当接收到Prestenr请求数据的时候,Repository就会从内存中取数据,如果没有就从本地取,本地没有就网络请求。
也不用担心每次请求到的数据都是缓存,因为Repository中还有一个变量判断数据是否过期。
这种Model层的设计方式还是很不错的,但是Repository中的代码看起就有点混乱了。
如果单个Repository中的数据量比较多的情况下,那么看起来会非常混乱,因为Repository中是管理了多个数据源的。
我觉得CleanArchitecture的model层设计比google的来的清晰,数据源用一个Factory类管理,并且用Cache类封装缓存,可以管理缓存的过期时间等。比起google直接在Repository中处理数据源逻辑要好些。
还有另外一点就是,有些数据是不需要本地保存的,每次都应该保证是最新的,例如新闻列表,这时候该怎么办。
我个人认为应该统一风格,就算不需要本地缓存,也务必使用多数据源结构的Repository,虽然类会多了,但是可以让你的项目更加清爽。
如果你的项目中,只有少数一两个接口是需要本地缓存的,那么还是不要用多数据源的设计了。这是过度设计,画蛇添足。
五、总结
googlesample的MVP架构用来学习的话是很不错的例子,至少比用其他人过度封装的mvp要简单点。
但是要在实际开发中使用mvp架构,最好是参照google和clean Architecture这两个比较热门的例子,然后花个几天时间认真思考才动手。
Model层,Presenter层的设计,还有统一处理公共参数,统一处理异常等都是值得思考的东西。