反应式编程 RxJava 设计原理解析
本文首发于 vivo互联网技术 微信公众号
链接: https://mp.weixin.qq.com/s/duO1pAfaKUI2_x_GVvZHMg
作者:Yunjie Ma
一、ReactiveX 与 RxJava
ReactiveX 的全称为Reactive Extension,一般缩写为 Rx,即我们平常所说的反应式编程。其设计原理主要使用了观察者模式,区分数据的生产者和消费者,通过事件流的方式进行数据的异步处理。
RxJava 是 ReactiveX Java语言的实现,其编程体验与Java 8中的函数式编程和流(Stream)有很大的相似之处,在掌握了Java8的相关知识后,你可以很轻松的就上手使用 RxJava。
本篇文章主要聚焦对RxJava中几种主要的设计模式的理解,通过梳理Observable的相关类图以及讲解这些类之间的关系,让大家能够更清晰的理解RxJava中事件驱动的工作原理。
二、RxJava中的概念
首先我们写一个简单的RxJava的程序,把数组中的元素作为事件发送,最终由消费者打印在控制台:
我们以这段简单的代码为基础,讲解下贯穿整个ReactiveX设计的四个概念:观察者,被观察者,事件,订阅。
观察者:对事件进行响应的对象,也可以称作消费者,在上述的代码中,subscirbe方法的参数是一个Consumer对象,该对象后续会被包装成一个LambdaObserver对象,即为这段代码中的观察者(消费者)。
被观察者:产生事件的对象,也可以称作生产者,在上述代码中,Observable.fromArray(...)返回的是一个Observable对象,即为这段程序的被观察者(生产者)。
事件:RxJava中存在四种事件流:onSubscribe(订阅事件),onNext(正常事件),onError(异常事件),onComplete(完成事件)。在上述代码中,是将数组中的元素作为onNext事件中的数据进行发送。
订阅:创建观察者与被观察者之间观察关系,对应着上述代码中的subscribe()方法。RxJava的事件驱动模型是一种“拉模型”,在观察者没有进行事件订阅之前是不会有事件产生的,只有观察者进行订阅后,才会触发被观察者生产事件。
对上述代码进行时序分析,可以清晰的看出这一段代码的运行过程,最终由FromArrayDisposable生产了onNext和onComplete事件,并通知Observer进行消费。
与此同时,我们也看到,简单的一行代码,竟然涉及这么多类的交互,如果增加一些其他的操作符,我们对整个程序把控起来就没那么容易了,下面我们将通过分析RxJava中的一些主要的设计模式,剖析类与类的关联关系,来更清晰地理解RxJava的工作原理。
三、 集大成者Observable
在整个数据处理的过程中,Observable可以说是最重要的一个对象。从上面的时序图可以看出,客户端(消息的生产者或者消费者)只和Observable进行交互,观察者和被观察者之间关系的创建也是由Observable去实现,而不用我们显示的编码实现,这大大降低了我们使用观察者模式的成本。
那么Observable主要有哪些作用呢,我们首先来看下和Observable相关的类图:
从图中我们可以看出:
Observable实现了ObservableSource接口,从字面意思就可以理解,这是一个提供观察能力的接口,所以Observable的一大能力是供观察者进行事件订阅,而进行事件订阅的方法实现就是调用Observable的subscribe()方法
Observable是一个抽象类,它提供了subscribeActual模板方法供子类实现,从源码中可以看出,Observable的subscribe()方法最终会委托子类的subscribeActual()方法实现,这个方法会建立生产者与消费者之间的关联关系。
除此之外,Observable还是一个工厂类,它提供了静态方法fromArray()、create()等用来创建具体的可观察对象,同时还提供了flatMap()、concatMap()等操作方法对可观察对象进行包装。
Observable的存在让生产者和消费者完全的解耦了,生产者只需关注自己生成何种Observable对象,而消费者也只需关注自己观察的是哪种Observable。
在实际的应用中,Rxjava已经提供了各种各样的操作符供我们使用,生产者只需要调用Observable中相应的方法即可以生成所需的可观察对象,供消费者进行事件订阅。消费者只需调用可观察对象的subscribe()方法即可与生产者建立观察关系,极其方便。
四、 真实的观察
观察者模式是RxJava设计的核心思想,在观察者模式中总是存在观察的对象和被观察的对象,从上文的解析中也可以看出Observable更多的是一个控制器的作用,而并非真正的事件的来源。那么在RxJava中,什么才是真正的生产者,什么才是真正的消费者呢。
我们来分析下以下三种常见的Observable:
先简单介绍下这几个Observable的作用,fromArray的作用是将数组中的元素作为onNext事件发送,create的作用是发送自定义事件,just的作用是发送单个事件。
上一小节有讲到实际的订阅行为是由各个Observable类中subscribeActual()方法实现的,我们来看下这三个类的subscribeActual()方法。
除去细枝末节,这三个方法都可以分成以下三步
创建被观察者对象,并传入观察者observer,建立两者的关联关系;
触发onSubscribe事件,观察者响应该事件;
进行事件的拉取,我们可以进入到d.run(),source.subscribe(parent),sd.run()这些方法的内部看一些,可以看到这些方法就是在发送onNext(),onError(),onComplete()等事件。
下图是整个流程中的相关类图。实际事件的发送者是FromArrayDisposable等对象,而实际的观察者,则是一个实现了Observer接口的实体类。如果我们在subscribe时传入的是一个lambda表达式,之后会被包装成一个默认的LambdaObserver对象,进行事件消费。
五、 包装的必要
RxJava中提供了丰富的操作符,比如flatMap,concatMap等可以对事件转换,subscribeOn,observableOn等可以对生产和消费的线程进行控制。这些操作符实际上调用了Observable中的包装方法对原有的可观察对象进行包装,返回了一个增强了的可观察对象。
操作符种类繁多,在这就不一一举例,我们以flatMap为例,分析一下这些操作符是如何工作的。
首先,flatMap操作会返回一个ObservableFlatMap对象,在创建这个对象时,会将原始的Observable对象作为构造函数的参数传入。
查看其核心方法subscribeActual,
可以看到这一类对象的subscribeActual方法和上一节中的方法不太一样,这里面并没有去实际的创建观察关系,而是做了两件事:
对观察者进行增强,将其包装成为MergeObserver对象,由其对产生的时间进行响应。
再调用source的subscribe方法,这里source就是前面构造函数中传入的Observable对象,由其再进行观察关系的建立。
下图是RxJava中装饰器模式的相关类图:所有的包装类都继承了AbstractObservableWithUpstream类,该抽象类有一个类型为ObservableSource的成员函数,用来持有被装饰的对象。
Observable是支持链式操作的,就和Java 8中的Stream一样,我们来考虑这样一行代码。
我们在分析上面这串代码时,一定会凌乱非常,在看源码时也会看到前面忘掉后面,但是如果我们对RxJava的包装流程足够了解的话,就可以很轻松的对上述代码进行分析。
六、 小结
RxJava的封装足够强大,可以让我们很方便的进行使用和扩展,但这也给我们理解其真实的工作原理带来了难度,如果我们对整个事件的处理过程处于一知半解的状态,那我们就无法从容的对服务进行异步编排,在实际开发过程中也难以发现问题的根源。
本文主要分析了RxJava中主要的设计模式,其中有模板模式、工厂模式、观察者模式、装饰器模式,理解了这些设计模式,理解了RxJava中类与类的关系,我们就能够对整个事件的处理流程了然于胸,分析代码时也能够事半功倍。
更多内容敬请关注 vivo 互联网技术 微信公众号
注:转载文章请先与微信号:Labs2020 联系。
反应式编程 RxJava 设计原理解析的更多相关文章
- Spring IOC设计原理解析:本文乃学习整理参考而来
Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...
- Android中使用反应式编程RxJava
GitHut 地址: https://github.com/ReactiveX/RxAndroid (1)RxJava简介: RxJava 是一个在Java虚拟机上实现的响应式扩展库:提供了基于obs ...
- 函数响应式编程RxJava
RxJava 到底是什么 一个词:异步. RxJava 在 GitHub 主页上的自我介绍是 "a library for composing asynchronous and event- ...
- RabbitMQ设计原理解析
背景 RabbitMQ现在用的也比较多,但是没有过去那么多啦.现在很多的流行或者常用技术或者思路都是从过去的思路中演变而来的.了解一些过去的技术,对有些人来说可能会产生众里寻他千百度的顿悟,加深对技术 ...
- 图解kafka - 设计原理解析
什么是消息队列? 简单来说,消息队列是存放消息的容器.客户端可以将消息发送到消息服务器,也可以从消息服务器获取消息. 问题导读: ********* 为什么需要消息系统? kafka架构? kafka ...
- MyBatis源码解析【7】接口式编程
前言 这个分类比较连续,如果这里看不懂,或者第一次看,请回顾之前的博客 http://www.cnblogs.com/linkstar/category/1027239.html 修改例子 在我们实际 ...
- iOS开发技巧系列---使用链式编程和Block来实现UIAlertView
UIAlertView是iOS开发过程中最常用的控件之一,是提醒用户做出选择最主要的工具.在iOS8及后来的系统中,苹果更推荐使用UIAlertController来代替UIAlertView.所以本 ...
- jQuery——链式编程与隐式迭代
链式编程 1.原理:return this; 2.通常情况下,只有设置操作才能把链式编程延续下去.因为获取操作的时候,会返回获取到的相应的值,无法返回 this. 3.end():结束当前链最近的一次 ...
- vertx 异步编程指南 step8-使用RxJava进行反应式编程
vertx 异步编程指南 step8-使用RxJava进行反应式编程 2018-04-23 13:15:32 zyydecsdn 阅读数 1212 收藏 更多 分类专栏: vertx 到目前为止 ...
- RxJava——响应式编程
自从06年开始,Rxandroid公司项目中陆续就开始使用它了,而它的基础是由Rxjava演变过来的,如今它也是越来越被广泛使用在商业项目中了,而做为"专业"的自己还是一直对它一知 ...
随机推荐
- Quartz核心原理之架构及基本元素介绍
1 什么是Quartz Quartz是一个作业调度框架,它可以与J2EE和J2SE应用相结合,也可以单独使用.它能够创建多个甚至数万个jobs这样复杂的程序,jobs可以做成标准的java组件或EJB ...
- Linux笔记03: Linux常用命令_3.1命令的基本格式
3.1命令的基本格式 3.1.1 命令提示符 [root@localhost ~]# 这就是Linux系统的命令提示符.各部分含义如下: ●[]:这是提示符的分隔符号,没有特殊含义. ●root:显示 ...
- JavaScript String对象及方法总结
String 对象创建方法: new String() var txt1 = new String("string"); var txt2 = "string" ...
- 万界星空科技五金家具企业MES案例介绍
五金家具行业MES解决方案 MES系统如何与家具企业生产相匹配?相较于其它大多数工业软件,MES系统无疑是受企业欢迎的软件之一.MES系统处于制造生产企业信息化的核心领域,有着承上启下的作用.那MES ...
- sudo: unable to execute /bin/rm: Argument list too long
Linux,删除文件夹下所有内容,数据太多时,报错too long sudo rm -r /var/lib/jenkins/workspace/test_1/allure-report/data/at ...
- 现代 CMake 模块化项目管理指南
现代 CMake 模块化项目管理指南 参考小彭老师的视频教程整理笔记,学习同时方便快速查阅,视频链接如下 [公开课]现代 CMake 模块化项目管理指南[C/C++] 对应课程 PPT 和源码见 ht ...
- java框架Mybatis的第一个程序
1:什么是MyBatis MyBatis 是一款优秀的持久层框架 MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程 MyBatis 可以使用简单的 XML 或注解来配 ...
- RocketMQ 的基本使用
RocketMQwiki是一个分布式消息和流数据平台,具有低延迟.高性能.高可靠性.万亿级容量和灵活的可扩展性.RocketMQ是2012年阿里巴巴开源的第三代分布式消息中间件,2016年11月21日 ...
- 文心一言 VS 讯飞星火 VS chatgpt (137)-- 算法导论11.3 3题
三.用go语言,考虑除法散列法的另一种版本,其中 h(k) = k mod m,m=$2^p-1$,k为按基数 $2^p$ 表示的字符串.试证明:如果串可由串 y 通过其自身的字符置换排列导出,则x和 ...
- GDAL数据集写入空间坐标参考
目录 1. 概述 2. 栅格数据 3. 矢量数据 1. 概述 可以通过GDAL给地理数据写入空间参考信息,不过要注意的是GDAL给矢量数据和栅格数据写入空间坐标参考的接口不太一样. 2. 栅格数据 实 ...