我们经常在讨论Android的架构,MVC,MVP,或者MVVM,有些同学觉得架构好App就做得好,其实这是错误的,我们归根结底还是需要找到自己合适的架构,适合自己的才是最好的,所以如何选择适合自己的架构才是关键。
《银河补习班》中有一句话:“人生就像射箭,梦想就像箭靶子,如果连箭靶子也找不到的话,你每天拉弓有什么意义?”
你得先了解你的需求,你的功能,你的可扩展方向,你才能知道,哪个架构适合你,同样你也要了解这些架构的优点,缺点,以及痛点,MVC能用这么久必然有它的道理,很多人舍弃MVC转而去使用MVP也必然有它的道理,既然里面这么多问题,这么多道道,我们想深度的了解,那么就需要刨根问底了。
先来说说MVC吧,分解出来,就是Model,View和Controller对吧,再转换一下就是模型,视图和控制器了,Model我们可以不用理会,主要来看下视图和控制器,控制器我们可以换一种说法,也就是逻辑,那么可以总结出:MVC就是视图与逻辑分离,再精简一下,View就是我们的XML,而逻辑就是我们的Activity或者Fragment等类,这样就好理解了吧,他们的分工是明确的,布局就负责UI,Activity就负责逻辑。
这套架构沿用了几十年,也深受各界的爱戴,然而随着互联网乃至IT界的高度发展,需求越来越多,代码越来越多,项目也越来越大的情况下,你会发现,哪怕你写得代码再美,再精简,各种管理类,帮助类,特别是Activity,异常的臃肿,几千行都不叫事儿,我上万行的都见过… ,而且你会发现一个问题,虽然架构叫MVC,但是View其实能做的工作特别少,各种控件的赋值,操作都是在Activity中完成的,倒不如干脆叫MC构架?(大家好,我是MC喜洋洋,青青草原我最狂~)
这个时候工程师们就意识到了,必须去做一些改变了,将逻辑层进行解耦,所以我现在回头看看MVP,你就明白他是干什么的了。
那么我们说到MVP,首先依旧是分解它,Model,View,以及Presenter ,你会发现,与MVC来讲,一个是C一个是P,也就是,模型,视图和接口层,P的职责就是连接View和Model并且对业务逻辑进行处理,相当于一个中转,Model将数据处理好之后通过Presenter传递给View去更新UI。
按照百度百科的说法:MVP是从经典的模式MVC演变而来,它们的基本思想有相通的地方 :Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。
所以按照我们正常的开发思维,MVC就如图中所示:
当Model的值更改,则需要通过Controller去通知View修改,因为View是依附在Activity中,如果View发生更改,同样的会更改Activity的监听去修改Model的值,同样的,View中是持有Model的引用的,Model中也有View的引用控制,这样的话,显而易见,他们互相是可以调用的,首先不说这个架构是不是比较乱,单从互相引用来看,过程中的问题肯定会出现很多。
那么再来看下MVP,既然说MVP是MVC的变种,那么他是如何修复这种弊端的呢?我们来看下图片:
比较明显的两个不同点,首先是Controller替换成了Presenter,而Model与View则无法通信,所以一般其他道友总结会这样去画图:
所以MVP首当其冲的就是Model与View的解耦,其次就是Activity的解耦了,P作为桥梁去平摊Activity的逻辑,让Activity做到真真正正认认真真的只负责View视图显示,这个怎么理解呢?首先P持有M这个没问题吧,那么现在P需不需要持有View,也就是P与Activity的通信如何去通信,这里则需要使用到接口了,也就是ActivityInterface,P与V之间通过接口进行通信就很好的实现了他们之间的解耦了,而ActivityInterface是一个新名词,所以我们重新来梳理一下他们关系,这也是很多人学MVP搞不懂的地方,关于MVP的角色关系。
View 视图 可以理解为Activity/Fragment
ActivityInterface 视图接口,便于View与Presenter之间的交互
Presenter 作为View与Model之间的通信桥梁
Model 数据层
所以,注意呀,MVP会多一个接口角色,当然,有些人会给Model也写一些接口来解耦,这也是可以的。
那么长篇文字的赘述下,你是否有一些理解他们之间的区别了呢?不了解也没关系,现在还没到让你总结他们的优缺点以及对比的时候,现在开始进入实战吧,我们通过代码来理清楚这些逻辑岂不是印象更深刻?
“清华北大只是过程,不是目的”。面对儿子的长大后想干什么的回答,马皓文想让他知道,能读清华北大的人毕竟是少数,即使没有考上清华北大,人生也要有追求的目标,毫无目标的生活,就如没有箭靶子一样,每天拉弓就没有意义。
同样的,MVP也并不是什么新技术,更多的是思考以及开发思路的转变,我们来做一个很简单很简单的例子吧。
需求:通过OkHttp请求一个Url,通过Gson转换数据,Glide显示图片,RecyclerView显示数据
那么我们先来看下传统的MVC写法是如何实现的:
先说思路吧:先创建一个实体Bean,再创建一个Adapter,然后在Activity中初始化RecyclerView后,通过OkHttp请求数据后,填充到Adapter中,所以我们先看下实体类
我们只需要取ResultBean中的几个数据即可,再来看下Adapter
在适配器中,其实没什么特别,在onBindViewHolder中填充Bean的数据就是了,然后再来看下Activity的代码:
这是一份很简单的代码,人人都会,那么从中你可以看出MVC相关相关的逻辑吗?首先Model就是MvpRecyclerBean,这可以理解吧,然后就是View了,View的话你可以理解为布局,再看Controller,实际上在MVC的体系中他就相当于我们这个Demo中的MainActivity了,你可以发现,他们的关系就是相互的穿插。
通过这张图就可以反映出来。
我们也可以看下效果:
那么我们现在通过Mvp来进行优化,是如何去做的呢?
其实代码本身很简单,更多的是将传统的MVC思路给转换,架构体现的就是思想,我也尽量的通过语言来一直解释其中缘由,如果只是抛出代码,实际上没多少东西,但是你学完你也会发现,也没学到多少东西,所以吾日三省吾身,思路,思想,思考。
MVP的核心就是将UI逻辑抽象成View接口,将业务逻辑抽象成P的接口来体现,我们来看下这张图:
View通过接口向P获取数据,实际上P是向M获取数据,M获取到数据之后通过接口给P,P来处理后,告知View的UI逻辑。
你会发现,M和V是无法直接接触的,他们中间有P作为中转,很好的做到了各自的职责,那么我们来看下代码怎么改吧:
先来看下我创建的包名:
我们可以看到,我创建了model包下的两个类,分别是MainModel的实现以及IMainModel的接口实现,还有MainPresent的中转实现以及IMainView的UI视图实现。
再来看一张图:
这是MVP的核心思想,一定要仔细看,先说逻辑再说代码,首先,在MainActivity中会去创建MainPresent的实例,这样就可以调用MainPresent的loadData方法加载数据了,在MainPresent的loadData中实际上是创建了MainModel,通过MainModel来调用他的loadData加载数据,然后MainModel作为一个执行者去通过OkHttp拉取到数据之后通过IMainModel接口的OnResultListener返回给MainPresent,MainPresent又通过IMainView的showRecyclerview接口返回给MainActivity,然后MainActivity即更新UI即可。这逻辑多读几遍,然后来看代码:
第一步:在MainActivity中会去创建MainPresent的实例,这样就可以调用MainPresent的loadData方法加载数据了
第二步:在MainPresent的loadData中实际上是创建了MainModel,通过MainModel来调用他的loadData加载数据
第三步:MainModel作为一个执行者去通过OkHttp拉取到数据
第四步:之后通过IMainModel接口的OnResultListener返回给MainPresent
第五步:MainPresent又通过IMainView的showRecyclerview接口返回给MainActivity
第六步:然后MainActivity即更新UI即可
前后呼应,我已经把功能和代码完全解析了一遍,你只要认真读,学会不成问题,那么你现在可以发现,我们一共创建了四个类,要是加上MainActivity就五个类了,MainActivty全部通过Present去操作逻辑,而数据层则是通过P绑定的Model层来做的,这样你是不是有一种豁然开朗的感觉,没错,结合我们之前画的图,你理解就更加透彻了。
当然,如果你想增加更多的接口,比如showLoding,或者showErrorDialog什么的也是没问题的,在IMainView中增加即可,然后在Present中实现就好了。
写完这些代码之后你会不会产生疑惑,使用MVC我只要把OkHttp请求数据的代码块直接放在Activity,然后得到结果显示就好了,但是用了MVP你却让我这个转那个转这个的,这就涉及到了MVC和MVP的优缺点了
1.MVC和MVP都有自己的优势和劣势
2.小项目优先MVC,大项目优先MVP
3.MCV会让Activity十分的庞大,代码难懂
4.MVP可以分解单一职责,代码易懂,却增加了很多的类和接口
5.MVP高度解耦
接下来还有最后一个问题,关于内存泄漏的问题。
关于内存,我在之前的内存泄漏中告诉过大家如何去解决此类问题,这里就不一一赘述了,我们直奔主题吧。
MVP的优点很多,得益于P的思路转变,但是由于P是持有View和Model的引用,那么如果View,也就是Activity退出没有及时销毁,而P或者M都有可能在异步执行,这个时候内存肯定会出现泄漏乃至溢出的风险,那解决的办法也很简单,当Activity销毁的时候也销毁P就好了,但是V这么多,都得去写P吗?这就需要去封装一个BaseP了
可以看到,我在这里定义了一个P的基类软引用来处理V,然后需要我们的MainPresent继承它
这些没什么问题,关键还是在BaseActivity中的思路
可以看到这边抽取之后可以得到一个Present的对象,并且通过createPresenter让外部可以实现,其他的则绑定了他的生命周期,这样就很好的避免了内存的问题了。
而在Activity中我只要继承了BaseActivity即可
可以看到,现在我依旧可以通过BaseP中的mPresenter对象来进行loadData。
到此,MVP就讲解完了,实际上,从始至终,我都是想让你摆脱代码的束缚,掌握这种思想,代码只是一个实现过程。
源码及PPT:
密码:gfk9