本文来自于腾讯bugly开发者社区,非经做者赞成,请勿转载,原文地址:http://dev.qq.com/topic/57bfef673c1174283d60bac0前端
Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员都是通过审核的移动开发工程师。每周都会举行嘉宾分享,话题讨论等活动。java
本期,咱们邀请了腾讯IEG Android 开发工程师——戴俊,为你们分享《基于RxJava的一种MVP实现》。node
分享内容简介:react
RxJava是一个实现Java响应式编程的库,让异步事件以序列的形式组织。MVP则一般用来将View业务层与Model层分离开来,二者结合起来可轻松实现业务解耦、线程控制、单元测试等等强大功能android
内容大致框架:git
下面是本期分享内容整理github
Hello,你们好,我是戴俊。目前在IEG腾讯动漫主要负责Android端的开发工做。编程
第一次进行这种微信群的分享,若是有任何疑问,欢迎你们在分享结束后提问。下面开始咱们今天的分享。缓存
咱们知道原生Android开发已是一个基础的MVC框架,因此在项目刚开始开发的时候并无遇到太多问题。服务器
对一个经典的Android MVC框架来说,它的结构大概是下面这样(图片来自参考文献)
这样的结构下,Activity层既承担了View层的一部分工做(由于XML做为View层的一部分功能实在太弱了),又承担Controller层的工做,所以当业务变化时,Activity层会极剧膨胀。
拿咱们项目早期的例子,一个Activity曾经最多达到了2000到3000行,重构的时候极其痛苦。
要解决这个问题,主要的办法有两种:
两个方法最终要实现的都是解耦。分层讲的是纵向层面上的解耦,模块化则是横向上的解耦。
咱们今天要讨论的MVP就是一种经过分层来进行解耦的框架。
若是你是个习惯了读文档的老司机,能够直接参考下面几篇文章
固然若是以为看官方的示例太麻烦,那么下面咱们就来说解一下如何实现一个简单的MVP构架。
这是一个比较典型的MVP结构图(图片来自参考文献),相比于第一张图,多了两个层,一个是Presenter和DataManager层。
多出这两个层到底有什么做用,下面咱们来用代码说明。
首先咱们假设有一个从服务端获取字符串并显示的手机上的简单功能。下面是主界面的代码
Activity里面包含了几个文件,一个是View层的对外接口MainView,一个是P层的Presenter。
首先看View层的对外接口文件
由于这个功能比较简单,只须要在设备上显示一个字符串,因此只有一个接口方法onShowString(),再看P层代码
从上面三个文件能够看到,View层经过注册Listener将本身的接口MainView交给了Presenter, 而Presenter层持有Model层的也只是一个接口。经过Presenter层将业务层与展示层隔离了开来,这样的好处是什么?
咱们知道接口的一个做用一般是用来抽象行为,对外部屏蔽实现细节。因此对于View层来讲,业务细节被屏蔽了,对业务层来讲,展现细节被屏蔽了。而对于处于中间的Presenter层来讲,它就像一个接口拼装器,把View层发出的请求传递给业务层,把业务层返回的数据又送还给View层展现,至于先后两端怎么实现的,它才不用关心。
接口的第二个做用是能够用来切换实现。咱们先看下面的代码。
从上面三个文件能够看到,业务层对外的只有一个接口,实现却有两个(DataSourceImpl和DataSourceTestImpl)。从名字你们就能看出来有什么做用了,一个是正常环境的业务层实现,一个是测试环境的业务层实现。
这里咱们设想一个场景:
开发同窗接到一个新的需求,设计稿也输出完成了,然然后台的接口却迟迟没到,怎么办? 如今经过MVP,咱们把业务层实现切换到DataSourceTestImpl,是否是能够先本身假写数据,调好一切前端和交互,而后泡一杯咖啡等后台同窗把接口写完联调?或者有时候为了重现一个bug,要在线上写一条脏数据,测试完再删除?
相似的应用场景其实有很是很是多,这里咱们就看到了使用接口解耦的一个好处了,不只把业务层和展现层解耦开来,还把Android开发同其它的一切的外部数据依赖都解耦开来。
这里我想提到以前讨论过的单元测试问题,不少同窗反馈项目开发过程当中没有作过,或者没有时间精力去作单元测试,或者由于业务变化太大致使没法作单元测试。其实在咱们项目中也遇到过样的问题,但其实经过这样分层以后,才发现单元测试实际上是彻底能够推动的,也彻底不用再担忧测试的时候会把脏数据写到线上的问题了。
到如今为止一个基于MVP简单框架就搭建完成了,但其实还遗留了一个比较大的问题。
不少同窗可能已经发现了,Presenter层在调用业务层的时候是直接调用的,而Android规定,主线程是没法直接进行网络请求,会抛出NetworkOnMainThreadException异常。
因此在presenter层,咱们须要进行一项线程切换的工做,这样才能保证“全部的IO操做都应当在线程中完成,主线程只负责页面渲染的工做”这一优化准则。
固然,Android自己提供一些方案,好比下面这种:
经过新建子线程进行IO读写获取数据,而后经过主线程的Looper将结果经过传回主线程进行展现,这种方案是勉强也行得通的。
但问题也有,一是线程须要额外管理,不可能每次发请求都要开启一个线程;二是适应性差,假如数据请求有前后依赖,有并行的状况,这样的写法变得脏乱无比。
好在有了RxJava ,能够比较方便的解决这个问题。
RxJava是一个天生用来作异步的工具,相比AsyncTask,Handler等,它的优势就是简洁,无比的简洁。在Android中使用RxJava须要加入下面两个依赖。
compile 'io.reactivex:rxjava:1.0.14' compile 'io.reactivex:rxandroid:1.0.1'
这里咱们直接介绍如何使用RxJava解决这个问题,在presenter中修改方法getData()。
简单解释一下,dataAction是咱们的数据业务逻辑,viewAction是界面的显示逻辑,经过RxJava的传递和变换,dataAction会在由RxJava管理的IO线程--Schedulers.io() 中执行,而viewAction则会在UI线程--AndroidSchedulers.mainThread()中执行。
RxJava固然不止这么简单,还有别的玩法,比方说进入一个界面的时候,须要先加载缓存的数据,而后再从网络获取更新的数据进行刷新。有的时候,可能还须要处理IO过程当中的异常状况,加入RxJava的异常处理参数。
RxJava的使用场景远不止这些,线程变换、数据变换、接口顺序依赖、接口并发请求这些要求对它来讲都是小菜一碟。固然,有些同窗可能以为RxJava入手有些困难,代码也会变得不那么直观,但相信只要你们慢慢熟悉它以后,它就会变得无比讨人喜欢。
下面列出了一些常见的RxJava的经常使用场景,其实还有更多的其它功能等待着你们去挖掘。
上面这些功能均可以经过RxJava来轻松完成。具体的使用就再也不多讲了,你们能够参考下面的文章:(Google文章名就能够了)
1.给 Android 开发者的 RxJava 详解 2.RxJava 与 Retrofit 结合的最佳实践 3.RxJava使用场景小结 4.How To Use RxJava
至此为止,经过MVP+RxJava的组合,咱们已经构建出一个比MVC更灵活的Android项目开发框架,好处大概有如下几点:
以上就是我今天的分享,内容上可能还有不足甚至不够好的地方,欢迎你们指出,一块儿讨论学习。
这里也顺便打个广告,欢迎你们下载腾讯动漫App,这里有最新最热的国漫日漫,支持正版,你我共享。
Q1:对于这样的一个MVP项目,它里面的包结构怎样规划比较清晰合理呢?
包结构的一般分法有两种:一种是按功能模块分,把某一个功能的presenter, activity,view层接口放到一块儿;一种是按类型分,P层M层和V层分红三个包。实际项目应用,我我的倾向于第一种,这种不管是开发过程,仍是排查问题都会方便不少。固然,不一样的项目仍是有不一样的分法的,不一而论。
Q2:耗时操做可能引发的内存泄露问题,请问是如何处理的。 Q3:用mvp时,请问大家在哪里释放一些引用,防止内存泄露的 Q4:p持有v的引用,请问怎么解决Activity的内存泄露问题? Q5:网特别慢的时候,应用退出,但网络请求还没结束,p层回调持有上下文形成内存泄露,通常怎么解决啊。
这几个问题其实比较相似,咱们在实际项目中,presenter会随着activity的生命周期进行销毁,好比在onDestroy方法中对presenter进行置空和引用解绑, 固然咱们能够给全部的Presenter写一个共有父类BasePresenter,专门来处理这个问题。
Q6:需求包含列表页的时候,列表项也是按照mvp的思想来分层,仍是封装成模块比较合适
目前咱们的作法是直接封装成模块,简单的问题不宜过分设计
Q7:想问一下腾讯动漫这个app目前用的就是您讲的这个架构吗,在实际用的过程当中有遇到什么问题吗
是的,咱们已经使用了这个架构。实际使用过程当中,常常会纠结的问题是业务逻辑层要不要再次独立分层。
Q8:项目中作测试是好事,但我以为建议去掉TestImpl测试文件。若是项目打包时,打到包里,会致使包变大,这种测试建议用node写个简单的服务,不知道嘉宾你咋看?
是的。正式项目中,能够经过注解,或者proguard或者gradle的配置将这些测试文件不打到包里。Node写服务的话是否是又要搭环境,这里的作法就是不使用任何外部环境依赖。
Q9:mvp通常都是activity和Fragment加入presenter层,那么列表adapter里的逻辑是否也要加上presenter层呢
Adapter其实跟View更接近的一个东西,它是用来处理重复显示问题。通常来讲,咱们传给adapter的数据无缺能直接显示的,建议在业务逻辑层将数据拼装好再传进去。 答:Adapter其实跟View更接近的一个东西,它是用来处理重复显示问题。通常来讲,咱们传给adapter的数据无缺能直接显示的,建议在业务逻辑层将数据拼装好再传进去。
Q10:咱们项目中采用了MVP可是没有用RxJava,m与p层采用回调方式,这样m经过回调间接引用p,p层有v的引用。若是在网络状况很差频繁打开关闭页面在网络请求结束前是否会有内存泄漏问题。rx是否能解决这个问题。还有当网络结束回调时v对应activity destory了怎么办。每次都去判断activity状态吗?
Rx不能解决内存泄漏的问题,前面2.3.7问题都提到了,一般的作法是在activity层销毁的时候进行解绑。回调时activity destory的话,咱们如今的作法是对view层接口进行一次空值断定。若是有更好的办法,也欢迎你们提出来讨论
Q11:有时候例如自定义view依赖于服务器返回的model,里面也有不少根据model属性去绘制的过程,这种状况怎么处理?在P层抛出一个model的get方法吗?
自定义的View跟Activity同样,咱们统称为View层。上面的例子中View层只有一个接口MainView,实际项目中,View层可能会实现好几个接口。对一个常常会被利用的自定义View,会额外给它新建一个接口。
Q12:你的例子中p层实现中getDate()方法对数据进行了处理,是否m层只是单纯的获取原始数据,对于数据上的业务也放入到p层中处理,有没有好的方式可以复用有关数据业务的这块逻辑
嗯,这个问题咱们确实也遇到了。在项目实际操做过程当中,若是有比较复杂业务流程,我会单独再分离出一层业务层,业务层再去调用dataSource取数据。若是只是单纯的取数据展现,如今这样就够了,尽可能避免过分设计。
Q13:为了更好的解偶每一层,大家用MVP时 是否每层都有本身的数据结构,若是有的话,层与层之间的数据结构转换开销大不大?
目前来说,大部分的业务都是一个数据结构穿透使用的,偶尔会有数据结构从新封装, 影响不大。我我的判断的话,相比IO处理,数据结构的转换开销仍是小的,并且,若是有不少复杂转换的话,保证不要在UI线程中作,也不会太大问题。
Q14:activity与p层用接口的方式衔接的价值在哪?另外如何界定展示方法在哪调用?好比页面须要显示一个标题,内容是从以前页面传过来的,那是在activity接收后就直接显示?仍是先传递到p层再回调activity的显示方法?感谢
价值在于,把presenter 与activity解耦以后,我能够在别的activity使用这个presenter层逻辑,也能够在这个activity 里调用其它页面的presenter方法。若是是前页传过来的,直接显示就好,不作过分设计。
Q15:rxJava使用lamaba的语法格式的话貌似会将代码缩减不少,请问嘉宾有试过这种方式吗?这个对项目的性能会有什么影响吗?由于我试用过几回后一直出现oom的问题
lambda表达式会让语法看起来更简洁,很是推荐使用。但咱们的项目目前只能使用jdk 7,悲伤。若是后面咱们有机会切换的话,能够再一块儿分享一下。
Q16:rxjava怎么实现队列像handler message那样,就是队列执行,不是并发执行?
rxJava中的just方法和from方法都是以队列形式发出事件。我猜你想问的问题多是:一个接口的请求依赖另外一个API请求返回的数据,这就是嵌套回调问题。能够找下大头鬼Bruce的一篇文章,《RxJava使用场景小结》,里面有介绍的,这里不详细讨论了。
更多精彩内容欢迎关注bugly的微信公众帐号:
腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的状况以及解决方案。智能合并功能帮助开发同窗把天天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同窗定位到出问题的代码行,实时上报能够在发布后快速的了解应用的质量状况,适配最新的 iOS, Android 官方操做系统,鹅厂的工程师都在使用,快来加入咱们吧!