前言
在前面的文章中,我们已经介绍了,
- 关于RxJava的[响应式编程思想](我们为什么要学RxJava?【RxJava系列之问个为什么】 - 掘金 (juejin.cn))
- [基本使用](RxJava的今生【RxJava系列之基本使用】 - 掘金 (juejin.cn))
- 以及[设计模式](RxJava的前世【RxJava系列之设计模式】 - 掘金 (juejin.cn))
相信大家对RxJava的基础知识,已经是了如指掌了。
接下来,就让我们对RxJava的原理,一探究竟!
本篇文章致力成为全网最通俗易懂最细致的RxJava原理讲解文章
本篇文章将会带你手写RxJava核心原理
一. 原理是什么
RxJava的原理,其实就是两个设计模式的应用。即观察者模式和装饰器模式。
二. 关于RxJava的观察者模式
普通的观察者模式
我们知道,普通的观察者模式,都是观察者订阅被观察者 。同时一个被观察者可以对应多个观察者。举个很形象的例子:电灯和开关。
按下开关,电灯就亮。在这里,就是"电灯"在观察着"开关",即观察者订阅被观察者的变化。当电灯观察到开关被按下时,就会做出亮灯的动作。同时,一个开关还可以控制多个灯泡,即一个被观察者可以同时对应多个观察者。
这是一个很常见的例子,相信不难理解。
RxJava的观察者模式
可是RxJava的观察者模式,和普遍的观察者模式有两点不同。一是被观察者订阅观察者,二是一个被观察者通常只对应一个观察者。
但,它的基本思想内核,还是观察者模式,只是形式不同而已。
比如这个"被观察者订阅观察者",听起来感觉比较奇怪,但为什么会是这样呢?原因很简单,就是为了保持代码链式调用的连续性,保持响应式编程的特点,不让它中断。
我们平时总说,要让用户体验流畅,不让它卡住或者中断。对于RxJava来说,我们就是RxJava口中的用户,所以他们也非常照顾我们的"用户体验"
比如下面这个例子,
蓝圈部分是被观察者 的部分,绿圈部分是观察者 的部分,中间红框就是订阅方法。可以看到,这样写就能够保证中间不中断,事件是从被观察者"流"出来的,更能突出响应式编程的特点。
类比开关和灯泡的例子,就是说,开关订阅了灯泡,开关发生了变化,推动着灯泡亮,开关成为了主导者,即被观察者是事件的上游,推动事件流的发展 。而不是观察者在下游一直盯着,耗费人力物力,ROI很低。(这个其实就是响应式编程的特点,具体可看[这一篇文章](我们为什么要学RxJava?【RxJava系列之问个为什么】 - 掘金 (juejin.cn)))
综上,RxJava的观察者模式,是改装后的观察者模式,是更适合RxJava框架的观察者模式。
三. RxJava的装饰器模式
上篇文章也讲过了装饰器模式,讲的非常详细,反而有可能会让你听不太懂。所以在这里我又对装饰器模式进行了精简,抽出其核心思想,方便我们理解RxJava的装饰器模式。
普通的装饰器模式
举个实际的例子,
(1)首先,有一个接口,表示某样东西"可以喝"
(2)然后有一个具体实现类,比如咖啡
这个类其实用不到,放到这里只是方便理解。
到这里,就是一个很普通的例子:一个接口,同时还有一个接口实现类,无任何理解压力。接下来就到了装饰器模式的部分了
(3)一个针对咖啡的装饰器类(抽象类)
注意这里的注释,很重要,即本身要实现接口,而且还要能装下一个接口 。本身实现接口的目的之一就是能够灵活实现可插拔的效果(可看[上一篇文章](RxJava的前世【RxJava系列之设计模式】 - 掘金 (juejin.cn))关于装饰器模式的介绍),以方便自己被另外一个装饰器类装下,其实还是服务于目的一。装下一个接口的目的就是,利用装下的这个接口,实现对装下的这个接口的功能的增强,因为这个接口,就是其他的装饰器或者具体类
(4)比如咖啡,有两个具体的包装类
在drink
方法里面,没有更改drinkable
的drink
方法内部的逻辑,而且还对这个方法进行了功能的增强。这就是装饰器模式的核心理念。
(5)具体使用
可以看到,包装类要能装下一个drinkable
的作用:就是能够装下具体类,或者具体包装类,从而实现对其功能的增强。在这里,coffee
就是具体类,coffeeWithCup
就是具体包装类,因为他们都实现了Drinkable
接口,所以就可以被包装类持有,从而进行功能的增强。
上面的类,包装后的结果就是这样:
让我再来梳理一下这几个类的关系。
首先,有一个接口,表示某种东西"可以喝":Drinkable
然后,有一个具体的类,实现了这个接口:Coffee
然后,需要对这个具体类进行包装。因为包装可以有多种包装,即包装类可能会有多个,所以这里采用抽象类的形式:CoffeeDecorator
。这个抽象类有一个特点,就是本身实现了Drinkable
接口,然后还能装下一个Drinkable
接口 。本身是Drinkable
接口,其实也是为了能够"装下一个Drinkable
"服务。因为如果本身没有实现Drinkable
接口,那就不能被其他装饰类装下了。
然后,就是两个具体的包装类,这两个具体的包装类,继承了CoffeeDecorator
。也可以把他们就理解成还是CoffeeDecorator
。
最后,在使用上。我们先创建了一个Coffee
,然后包装它两层,最终实现如上图所示的效果。
我相信我已经解释的非常清晰了,你必须保证你完全理解上述装饰器模式的内容,特别是这个例子中几个类的关系。这对理解RxJava的装饰器模式至关重要。
RxJava的装饰器模式
我们都知道了,RxJava有两大角色,一是观察者Observer,二是被观察者Observable。RxJava的装饰器模式就是对这两大角色分别进行的包装。即对Observer有包装,对Observable也有包装。为什么要进行包装呢?很简单,想想装饰器模式的定义,就是为了增强原有功能。比如让RxJava引以为傲的线程切换功能,就是通过装饰器模式,外面套了一层,实现了线程切换,即"增强了原有功能",来实现的。
针对Observable的装饰器模式
我们先看对Observable的包装,下面是之前讲过的,一个标准的RxJava的使用示范
我们重点关注,这三个圆圈部分。这三个圆圈部分,都是被观察者。在这里,就是:创建一个被观察者,就套一层 。比如在这里,调用完Observable.create
后,就生成了ObservableCreate
对象。
然后调用了subscribeOn
后,就生成了ObservableSubscribeOn
对象,
调用了observeOn
后,就生成了ObservableObserveOn
对象。
这三个对象,都是包装类的子类,这个包装类就是:
在上文中我们说到,一个包装类,必须要实现一个接口,还要装下一个接口。那么这个神秘的接口,就是
点进去看接口的定义,就是
OK,看到这,你也许会不太清晰,不要紧,只要你理解了一开始我举的例子,那你一定能理解这里,因为这几乎就是照搬!不信?让我给你画个图
怎么样,是不是一模一样!
当然,有一个微小的不同。即Coffee
是具体类,而ObservableCreate
是包装类。这俩严格意义上来说,不是对应的,但不影响我们理解。什么?你想看严格对应的?OK,我来试图画一下
突然变得复杂了...,不过,在这个图中,你可以很清晰地看到,RxJava的Observable的装饰器模式的内核了。同时你会发现,ObservableOnSubscribe
理应和Coffee
对应,也实现ObservableSource
接口。但实际上不是。它本身就是一个接口,它的定义是这样的
而ObservableSource
接口的定义,是这样的
你会发现,尽管参数不一样,但这俩接口,基本大差不差,基本上是一样的。那为什么要做这一层区分呢?我猜测是这样的:
为了区分不同subscribe
的功能 。ObservableSource
的subscribe
,突出的是订阅功能,即发起订阅,实现观察者和被观察者之间的订阅关系,这就是它的subscribe
主要突出的作用。
而ObservableOnSubscribe
的subscribe
方法,突出的是发送事件功能。即被观察者的事件,如何发送到观察者,怎么发送到观察者。这是它侧重的功能。
让我们再看一下标准使用范例中,两个subscribe是怎么用的
这就是我理解的,这两个subscribe
的区别
总结一下,Observable,是创建一次,包装一次 。创建后得到的类名,都是以Observable为前缀的,后缀,与创建方法相对应。比如调用了create
方法,就创建了ObservableCreate
包装类,调用了subscribeOn
方法,就创建了ObservableSubscribeOn
方法,以此类推。
针对Observer的装饰器模式
让我们看下Observer是如何被包装的。回归到标准使用范例中,当我们调用了这个subscribe
方法时,会发生什么呢?
subscribe
方法是抽象类Observable的方法,它实现了通用的逻辑,并调用了子类的subscribeActual
方法,如下图
在这里,因为前面调用了observeOn
方法,创建了ObservableObserveOn
对象,所以在这里就是调用的ObservableObserveOn
对象的subscribeActual
方法,让我们进去看一下
这里的source
,就是上一层包装类,即ObservableSubscribeOn
。同时在这里,我们发现,它把Observer进行了一次包装,包装成了ObserveOnObserver
类,然后传给了 ObservableSubscribeOn
。然后我们进入ObservableSubscribeOn
的subscribeActual
方法
这里也对observer进行了包装,把它包装成了SubscribeOnObserver
对象,然后我们进入到这里面
发现它还是一样,调用了source
的subscribe
方法
ObservableSubscribeOn
的source
是ObservableCreate
。所以让我们再进入到ObservableCreate
的subscribeActual
方法来看一下
还是老一套,但这里没有把Observer封装成类似CreateObserver
这样的对象,而是CreateEmitter
。然后它的source
,就是最开始的ObservableOnSubscribe
了。调用它的subscribe
,就是调用这里:
好,以上我们非常详细地捋了一遍Observer的包装过程。我们发现,它是在被观察者调用subscribe
的时候,一层一层地包装的。
让我们像上面一样,画图来捋一下它和我举的例子的对应关系
这里也类似Observable的包装过程,虽然总体思想相通,但是它还是没有完全和我举的例子对应,就是因为有两个接口,一是Observer接口,二是Emitter接口。但是呢,又是惊人的相似。这两个接口,实现的功能也是,大差不差。不信,你看Observer接口
然后,你再看Emitter接口
看到没,基本是一样的。而至于为什么要做这样的区分,我觉得原因之一,也是因为有不同的分工。Emitter侧重于"发送事件",而Observer侧重于作为事件的接受者,接收事件 。所以一般发送事件的时候,是调用Emitter的onNext,onComplete
这些方法,然后再调用到Observer的onNext,onComplete
这些方法。就相当于Emitter把Observer包了一层。那么在Emitter里面就可以进行功能的增强,比如可以判断线程是否释放了之类的。那是不是这样的呢?看源码!让我们进入Emitter的实现类:CreateEmitter中
发现,确实就是这样!
总结一下,Observer的包装,是调用subscribe
方法的时候触发的,每次调用subscribe
方法,都会对Observer包一层 。包装后的类名,和Observable类似,是以Observer作为后缀,比如经过observeOn
后,就变成了ObserveOnObserver
。
什么线程切换,什么线程释放,都是在包装类里面实现的,比如我们看ObserveOnObserver
的onNext
方法
这里就封装了线程切换的逻辑。其他的功能增强点,都是一样的道理,都在包装类中!
OK,接下来的时间,让我们再来梳理一下,Observable和Observer之间的对应关系
这就是RxJava的装饰器模式!怎么样,是不是非常清晰了!
四. 手写RxJava核心原理
下面,我们来个更牛的,你从来没有见过的,即手写一个RxJava核心原理逻辑!
基本介绍
举个很容易理解的例子,我先用文字描述清楚,然后再用代码写出来。
有两个角色:灯和人,人观察灯,当观察到灯灭的时候,采取一系列行动。比如人发现灯灭了,那就要开灯。开灯之前,要先擦手,然后手干燥了,再检查下电源,然后才能执行开灯的动作。这个例子,是不是很好理解!
但是这中间蕴藏着一个大坑!就是涉及了四个装饰器!哪四个呢?
"能实现擦手动作的人",以及"能实现检查电源动作的人"。
同时呢,电灯也有与之对应的两个装饰器
"能被擦完手的人观察的灯","能被人检查电源的灯"
这四个装饰器,对不对!
在前面的内容中,我们聊过,装饰器模式,有几个对应的角色,一个是接口,一个是具体类,一个是装饰器类。
下面,我们代码来实现。
代码实现
(1)首先,关于灯的接口:
(2)然后,关于人的接口:
(其实我在有意模仿RxJava的风格)
然后,关于灯的具体类和人的具体类,我们就不写了。因为到时候可以直接new一个接口实现类,来代表具体类。(RxJava一般就是这么干的)
(3)然后是,关于灯的装饰器类:
哈哈哈,这回是不是很明显地能发现我在模仿RxJava。之前讲过,装饰器类有两个特点,一个是自己本身实现接口,然后还能装下一个接口 。这里的装饰器类是完全符合这个特点的。然后里面的subscribe
方法和subscribeActual
方法,也是模仿的RxJava的Observable
(4)然后,关于人的装饰器类:
也是符合"自己本身实现接口,还能装下一个接口"的特点。
以上,我们把人和灯,对应的接口和抽象装饰器类都介绍完了,我相信这里没有什么难度,你完全能理解。
下面,我们介绍具体的装饰器类,鉴于关于灯的装饰器有俩方法,看起来好像比较复杂(虽然其实很简单),为了防止你难以理解,我们先介绍关于人的具体装饰器类
(5)关于人的具体装饰器类:
装饰器类里面总共就只有一个方法,里面就两大块逻辑,一是实现功能的增强,二是调用原有的功能。这又和装饰器模式:不改变原有类的基础上实现功能的增强,相呼应。
欧克,下面我们来介绍一波
(6)关于灯的具体装饰器类:
乍一看,感觉好复杂,有种驴唇不对马嘴的感觉,但如果你认真看并且理解了我前面讲解的,关于观察者的包装过程,那这里就没有任何理解难度,因为这就是RxJava的被观察者的subscribeActual
方法的核心执行逻辑!
在subscribeActual
方法里面,首先进行了功能的增强(就是把传来的观察者再包一层),然后调用了上一层被观察者的subscribe
方法,继而调用到上一层被观察者的subscribeActual
方法。
(7)在使用的时候,就是这样调用:
执行结果为:
以上,我们完成了手写RxJava的核心逻辑!现在,我们来复盘一下,它和RxJava是怎么对应的。先从使用这里看起
这是在使用的时候,我们写的例子和RxJava的对比,怎么样,是不是几乎一模一样!
然后我们来分析,在源码上,这两者的相似之处
上下两图对比,我们发现,基本上也是一样的!
到这里,关于RxJava的核心原理介绍,就介绍完了。
五. 总结
本篇文章,一开始我介绍了RxJava的观察者模式,然后我非常详细地介绍了RxJava中,Observable和Observer的装饰器模式怎么应用的,最后,手写了一个RxJava核心原理代码,加深了你的印象。
到此为止,本专栏的内容也全部结束了,希望能够对你有一些帮助,同时下个专栏,我将会详细地讲解Handler。如果你认为我的水平还可以,讲解内容还算比较干货,可以留个关注哦!