来一场嘴撕RxJava的热恋

一. 介绍 RxJava应该说是一种编程框架,采用的模式是观察者模式。同时它也属于响应式编程,何为响应式编程?本人建议可以对比流媒体的播放,甚至是cs模式,我理解为一种简易的问答模式。
需要注意的是观察者模式是一对多的关系,不难理解,一个观察者可以观察很多的角色。因此我大胆的推测,因为是一对多的关系,那么观察者就不会承担太多的业务逻辑和繁琐的逻辑关系,否则他会被忙死,甚至还有可能导致接受数据混乱等不可预知的问题。
推测是否正确,请看下文的原理解说!
二. 原理解说 鉴于本人对部分源码的分析和demo的编写,对RxJava我就总结以下三点。
1. 实现 RxJava是一种观察者模式,如果是第一次听说这种模式,可以结合EventBus和广播,甚至可以联想到textview的点击事件来对比想象一下。
所谓观察者模式,首先得清楚几个角色,一旦观察关系产生必然会有所谓的观察者和被观察者。顾名思义,观察者是来监视被观察者的行为,发现有需要时就采取行动。回到刚才的点击事件,你可以把一个textview当成被观察者,listener当成观察者,setOnClickListener是建立两者关系的产生。讲到这你大概可能明白了那么一点。下面就来说说RxJava里面是怎样产生这种模式的。
首先观察者是Observer,被观察者是Observable,订阅关系是Subscriber。需要说一点的是observer不是狗仔队不会时时刻刻盯着Observable,相反他很懒很被动,他做的事情只有撒网和收网。即是一开始的订阅关系建立和接到被观察者的回调通知。
说多了都会困,明白了这么一点点就先冲击下视觉,看下面一张关系图吧。(备注:看图的时候结合源码)
来一场嘴撕RxJava的热恋
文章图片
图形解说:先不讲解图,一般来说我们最想知道的一个问题就是:观察者,被观察者和订阅者三者到底是怎么产生关系的,里面终究藏了多少猫腻?
回答:看图!对于观察者而言,这简直就是一尊老佛爷啊,都被动成这个样子了,他很被动的被强加关系。再看看Observable被观察者,看着都让人心疼,挂了多少枝枝叶叶的关系啊。
既然如此,来剥洋葱吧。首先看这个observer这个观察者懒货,为何这么懒,追到源码里面去:
来一场嘴撕RxJava的热恋
文章图片
如此看来懒是有原因的,所谓观察者就是一个接口包含了三个方法,也证实了我一开始的大胆猜测,观察者里不会存在太复杂的逻辑。其中核心方法是onNext方法,就是在被观察者里要触发的方法,。
来一场嘴撕RxJava的热恋
文章图片
看到上面你会大吃一惊!胡扯!调onNext方法的明明是Subscribler啊,先别发怒,不懂就要继续剥洋葱,你瞅瞅Subscriber的源码:
来一场嘴撕RxJava的热恋
文章图片
来一场嘴撕RxJava的热恋
文章图片
看到这些你还吼吗?你倒是吼啊?没错!subscriber原来是实现了Observer的类,并且多了几个方法,常用的是onStart方法,取消订阅和是否已经订阅,对于这几个方法我就不多说了,明白人都能看懂。
剥到这一层的时候,正常人的思维都会想到,那被观察者是不是和Subscriber存在关系?否则观察关系行不通啊?吓得我赶紧去Observable(被观察者)的源码证实一下:
来一场嘴撕RxJava的热恋
文章图片
艾玛,打脸了没找到,不过找到一个很类似的名字(圈红的类),不到黄河不死心,继续剥洋葱。
来一场嘴撕RxJava的热恋
文章图片
来一场嘴撕RxJava的热恋
文章图片
终于等到你,还好我没放弃!妖孽,隐藏的好深!看到这里恍然大悟,一切水落石出了!一句话总结,observable里一直持有subscriber的对象,并且在call方法回调的时候,让subscriber产生行为。而subscriber尽管改头换面了一些,但是穿上马甲龟还是龟,它本身就是对observer的实现。 从表面上看观察者(observer)懒得无力吐槽,实际上伪装了一种身份一直潜伏在被观察者里面。也正因为如此建立一种监视的关系,而抛给用户的只是一开始的两种订阅方式:

observable.subscribe(observer); // 是定义的oberver

observable.subscribe(subscriber); // 这个是定义的subscrible
其实,真相都在暗处!上文提到的所谓订阅关系(subscriber)其实也是观察者!
2. 异步 Android的多线程可以说是Android的灵魂,而多线程处理不当带来的坑也是让大家踩了又踩,我们最怕的就是内存泄漏,而偏偏多线程的处理不当就会造成潜在的风险。
RxJava的异步操作貌似封装的不错,至少表面上用起来自己的心里受到了一点点安慰,假装它内部不存在内存泄漏一说。我们来看看跟平常的handle和aysncTask有啥不一样的地方。
先抛一张图:
来一场嘴撕RxJava的热恋
文章图片
图形解说:从图可看出,线程的调度是被观察者指定的。通过两个方法分别指定不同的线程。图形里面包含两个调度方法subscribeOn(Schedulers.io())和observeOn(AndroidSchedulers.mainThread()),前者指的是订阅产生的所在线程,往往是新开启的线程,这里写的io是针对于io操作的线程,非io操作的处理最好不要放在此线程里。而后者则是回调所产生的线程,从方法名可以看出观察者醒了他觉得事都干完了,得处理结果了,而android里面的结果往往是展现在ui上,而ui的更新需要在主线程进行,因此一般回调的时候都会放在主线程操作。
除此之外,图上还有个关键的类Schedulers。这个就是线程的调度类,可以说所有的线程都被这个类操控着。进入源码可以看到,有三种不同的Schedule,分别是图中的computationScheduler,ioScheduler和newThreadScheduler。至于他三个的区别参考下面:
1. computationScheduler

特点:计算所使用的Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。这个Scheduler使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在computation()中,否则 I/O 操作的等待时间会浪费 CPU。
实现:
Scheduler c = RxJavaPlugins.getInstance().getSchedulersHook().getComputationScheduler();

if(c !=null) {
computationScheduler= c;
}else{
computationScheduler=newEventLoopsScheduler();
}
2. ioScheduler
特点:I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的Scheduler。行为模式和newThread()差不多,区别在于io()的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下io()比newThread()更有效率。不要把计算工作放在io()中,可以避免创建不必要的线程。
实现:
Scheduler io = RxJavaPlugins.getInstance().getSchedulersHook().getIOScheduler();

if(io !=null) {
ioScheduler= io;
}else{
ioScheduler=newCachedThreadScheduler();
}
3. newThreadScheduler
特点:总是启用新线程,并在新线程执行操作。
实现:
Scheduler io = RxJavaPlugins.getInstance().getSchedulersHook().getIOScheduler();
if(io !=null) {
ioScheduler= io;
}else{
ioScheduler=newCachedThreadScheduler();
}


实例:
来一场嘴撕RxJava的热恋
文章图片
总结:
异步的内部实现机制以newThreadSchedule为例其实是实现了Executor最终还是实现了runnable,但是对开发者而言不仅逻辑明了简单,使用起来也是很简单,从一定意义上来讲不见了到处使用handler的影子。
3. 对象灵活转换 说到这,大家都会想到map和flatmap。关于这两个东东网上有很多介绍,但是你会发现介绍的越仔细,你会越懵逼。那就跟着我的思路,我带你秒懂。
第一步,先看源码:
map核心源码:
来一场嘴撕RxJava的热恋
文章图片
flatmap核心源码:

来一场嘴撕RxJava的热恋
文章图片
lift源码:
来一场嘴撕RxJava的热恋
文章图片
说明:为什么我要强调这三个方法?对,你猜对了!因为我们扒进去一看,最终都跟这个lift有关系。既然都跟这个有关系,那就进去看看这个到底实现了什么玩意儿。尼玛!一堆泛型,先不管这些玩意,映入眼帘的是熟悉的Observable和熟悉的Subscriber。好!有这两个就够了!
仔细一看其实也没什么特比之处嘛,就是和正常的使用一样在被观察者里面调用call通知观察者。如此简单而已!不,等等!怎么还有一句:onSubscribe.call(st); 哦哦,问题来了,很多同学看到此处可能有点懵逼了。再仔细看一下 Observable也是new出来的,感情这两玩意都是新的啊。不错!就是新的!
第二部,实例:
例如,我有个用户实体类,但是类里面还有其他的子类。
第一个需求,我想得到用户实体
来一场嘴撕RxJava的热恋
文章图片
第二个需求,我想得到每一个用户实体的每一个子类的信息(说的那么绕,其实简单来讲就是类里面套类,我通常喜欢称之为类里面对应一个列表)
来一场嘴撕RxJava的热恋
文章图片
第三步,大白话总结:
看到以上两步,你可能还是没咋看的明白,觉得我带你们秒懂是吹牛。但是接下来的大白话绝对让你心服口服!再没讲大土话之前,我顺便提一句,RxJava在很多同学眼里受青睐的一个最重要的原因就是多变,所谓多变比如在map里面我new一个func1任意指定你的类型,call都会给我返回你想要的类型。其实,懂得人都明白为什么可以实现这样呢?进去Func1你会发现这个接口里面定义的call方法的返回值也是泛型。总之来说,之所以能多变就是因为它啥啥都支持泛型。
言归正传,该说说重点了!flit里面的正真实现和android事件分发几乎一毛一样,简而言之就是处理一件事的路径很深的话我就不想自己搞下去了,要么是因为懒要么是因为官大装逼,反正我看到本次操作完成不了我就开始分发,也可以称之为代理。总之遵循只处理一个层级的原则,因此就会看到有新的observable和subscriber产生。总之一句话,flit里面做到事情就是任务层层的派发,并且每一次调度这个方法都会产生新的观察者,可以理解为很多个代理在做同一件事,我再次大胆的猜测这样就会提高执行的效率,反正事多兵也多。 (这句话只是猜测,没有证实,有兴趣的朋友可以去研究下,通过时间的分析)
三.使用 使用很简单,在gradle里面添加如下两句:
compile'io.reactivex:rxjava:1.0.14'

compile'io.reactivex:rxandroid:1.0.1'
然后就是写你的观察者--observer和被观察者observable以及两者产生的订阅关系。
四.总结 RXjava是一种使用起来简单,并且支持lambda表达式,很受同学们喜欢。还能很好的和retrift结合使用,尤其是对多线程的处理,可以说是既简单又好理解。
【来一场嘴撕RxJava的热恋】总之,以上也仅仅十个人的学习总结,有不当之处还望指出,期待和你共同成长!

    推荐阅读