人们通常先在线性表尾部临时添加一个_一个使用Java虚拟机的可观察序列编写异步和基于事件的程序的库...

前言

RxJava是的Java虚拟机实现反应扩展:通过使用可观察的序列来编写异步和基于事件的程序的库。

它扩展了观察者模式支持数据/事件序列,并添加操作符,允许您以声明方式将序列组合在一起,同时抽象出对低级线程、同步、线程安全和并发数据结构等问题的关注。

版本2.x(Javadoc)

  • 单一依赖性:反应物流
  • 继续支持Java 6+ &机器人2.3+
  • 通过1.x周期中学习到的设计变更和反应流共享空间研究项目。
  • Java 8λ友好的应用编程接口
  • 对并发性的来源(线程、池、事件循环、光纤、参与者等)没有意见
  • 异步或同步执行
  • 参数化并发的虚拟时间和调度器

2.x版和1.x版将共存几年。他们将有不同的组id(io.reactivex.rxjava2相对io.reactivex)和名称空间(io.reactivex相对rx)。

参见维基文章中1.x版和2.x版之间的区别2.0有什么不同。有关RxJava的更多信息,请访问维基主页。

版本1.x

这1.x版本是截止到2018年3月31日。不会发生进一步的开发、支持、维护、PRs和更新。最新版本的Javadoc,1.3.8,将保持可访问。

入门指南

设置依赖关系

第一步是将RxJava 2包含到您的项目中,例如,作为一个梯度编译依赖项:

履行"io . reactivex . rxjava 2:rxjava:2 . x . y"

(请更换x和y最新版本号:)

你好,世界

第二是写你好,世界程序:

包裹 rxjava .示例;进口 io.reactivex.*;公众的 班级 地狱世界{公众的 静电 空的 主要的(线[]args){可流动的。只是("你好,世界")。订阅(系统。在外::println);}}

如果您的平台还不支持Java 8 lambdas,您必须创建一个消费者手动:

进口 反应凸函数消费者;可流动的。只是("你好,世界")。订阅(新建 消费者%3C线%3E(){@覆盖 公众的 空的 接受(线 s){系统。在外。println} });

基类

RxJava 2有几个基类,您可以在上面发现运算符:

  • 易流动的:0..支持反应流和背压的流量
  • 可观察到的反应性凸起:0..n流量,无背压,
  • io .反应凸.单个:恰好一个项目的流程或错误,
  • io.reactivex.Completable:没有项目但只有完成或错误信号的流程,
  • 也许:没有项目、只有一个项目或错误的流程。

一些术语

上游,下游

RxJava中的数据流由一个源、零个或多个中间步骤组成,后面跟着一个数据消费者或组合器步骤(其中该步骤通过某种方式负责消费数据流):

来源。操作员1()。操作员2()。操作员3()。订阅(消费者);来源。平面图(值-%3E来源。操作员1()。操作员2()。操作者3());

在这里,如果我们想象自己在操作员2,向左看向源头,叫做向上游。向右看订户/消费者,称为顺流的。当每个元素都写在单独的行上时,这一点通常会更加明显:

源操作器1()。订阅(消费者)

运动中的物体

在RxJava的文档中,发行,发出,项目,事件,信号,数据和消息被认为是同义词,表示沿着数据流行进的对象。

反压力

当数据流通过异步步骤时,每个步骤可以以不同的速度执行不同的事情。为了避免过多的这类步骤,这些步骤通常表现为由于临时缓冲或跳过/丢弃数据的需要而增加的内存使用,应用了所谓的背压,这是一种流量控制的形式,其中这些步骤可以表示它们准备处理多少项。这允许限制数据流的内存使用,在这种情况下,通常没有办法让一个步骤知道上游将向它发送多少项。

在RxJava中,专用的可流动的类别被指定支持背压可观察量专用于非背压操作(短序列、图形用户界面交互等)。)。其他类型,单身,可能和可完成的不支持背压,也不应该支持背压;总是有空间暂时存放一件物品。

装配时间

通过应用各种中间操作符来准备数据流发生在所谓的装配时间以下内容:

易流动%3C整数%3E流动= 可流动的。范围(1,5)。地图(v-%3Ev*五)。过滤器(v-%3Ev% 3 == 0);

此时,数据还没有流动,也没有副作用发生。

订阅时间

这是一种临时状态,当订阅()在内部建立处理步骤链的流程中调用:

流动。订阅(系统。在外::println)

这时订阅副作用被触发(参见取消订阅)。在这种状态下,一些源会立即阻止或开始发射项目。

运行时间

这是当流主动发出项目、错误或完成信号时的状态:

可观察量。创建(发射器-%3E{正在…(!发射器。isDisposed()) {长的时间= 系统。currentTimemillis();发射器。onNext(时间);如果(时间% 2 != 0){发射器。一个错误(新建 非法状态异常("奇怪的毫秒!");破裂;} } } })。订阅(系统。在外::println,可投掷的::printstackTrace);

实际上,这是上面给定示例的主体执行的时候。

简单的背景计算

RxJava的一个常见用例是在后台线程上运行一些计算、网络请求,并在用户界面线程上显示结果(或错误):

进口 io . reactivex . schedulers . schedulers;可流动的。fromCallable(()-%3E{线。睡眠(1000);//模仿昂贵的计算 返回 "完成的";})。订阅(调度程序。io())。观察(调度程序。single())。订阅(系统。在外::println,可投掷的::printstackTrace);线。睡眠(2000年);//%3C -等待流程结束

这种链接方法被称为流畅的应用编程接口类似于构建模式。然而,RxJava的反应类型是不可变的;每个方法调用都会返回一个新的可流动的增加了行为。为了说明,这个例子可以重写如下:

易流动%3C线%3E来源= 可流动的。fromCallable(()-%3E{线。睡眠(1000);//模仿昂贵的计算 返回 "完成的";});易流动%3C线%3E运行背景=来源。订阅(调度程序。io());易流动%3C线%3E显示前景=运行背景。观察(调度程序。single());显示前景。订阅(系统。在外::println,可投掷的::printstackTrace);线。睡眠(2000年);

通常,您可以通过以下方式将计算或阻塞输入输出移动到其他线程订阅。一旦数据准备好,您可以确保它们在前台或图形用户界面线程上通过观察。

调度程序

RxJava运算符不支持线s或ExecutorServices直接但是用所谓的调度程序它抽象出了统一应用程序接口背后的并发源。RxJava 2具有几个标准调度程序,可通过调度程序实用类。

  • 调度程序.计算():在后台对固定数量的专用线程运行计算密集型工作。大多数异步操作符都将此作为默认操作调度程序。
  • Schedulers.io():在一组动态变化的线程上运行类似输入输出或阻塞操作。
  • 调度程序。单一():以顺序和先进先出的方式在单个线程上运行工作。
  • 调度员。蹦床():在一个参与线程中以顺序和先进先出的方式运行工作,通常用于测试目的。

这些在所有的JVM平台上都有,但是一些特定的平台,比如安卓,有它们自己的典型调度程序s定义:AndroidSchedulers.mainThread(),SwingScheduler.instance()或者JavaFXSchedulers.gui()。

此外,还有包装现有执行者(及其子类型,如ExecutorService)变成一个调度程序通过调度程序。来自(执行者)。例如,这可以用于拥有更大但仍然固定的线程池(不同于计算()和io()分别)。

这thread . sleep(2000);最终不是偶然的。在RxJava中,默认值为调度程序这意味着一旦Java主线程退出,它们都会被停止,后台计算可能永远不会发生。在本例中,睡眠一段时间可以让您在控制台上看到有空闲时间的流输出。

流中的并发性

RxJava中的流本质上是顺序的,分为可能运行的处理阶段同时地彼此之间:

可流动的。范围(1,10)。观察(调度程序。计算())。地图(v-%3Ev*五)。阻止订阅(系统。在外::println);

此示例流程将上1到10的数字平方计算 调度程序并在“主”线程(更准确地说,调用方线程封锁订阅)。然而,λ五-%3E五*对于此流不并行运行;它在同一个计算线程上一个接一个地接收值1到10。

并行处理

并行处理数字1到10要复杂得多:

可流动的。范围(1,10)。平面图(v-%3E 可流动的。只是(v)。订阅(调度程序。计算())。地图(w-%3Ew*w))。阻止订阅(系统。在外::println);

实际上,RxJava中的并行意味着运行独立的流,并将它们的结果合并回单个流中。操作员平面地图这是通过首先将1到10的每个数字映射到它自己的个体来实现的可流动的,运行它们并合并计算出的方块。

然而,请注意平面地图不保证任何顺序,内部流的最终结果可能会交错出现。还有其他操作符:

  • 连环地图一次映射并运行一个内部流程
  • 拼接渴望它“一次”运行所有内部流,但是输出流将按照创建这些内部流的顺序排列。

或者可流动的平行()操作员和并行流动类型帮助实现相同的并行处理模式:

可流动的。范围(1,10)。parallel()。runOn(调度程序。计算())。地图(v-%3Ev*五)。顺序()。阻止订阅(系统。在外::println);

从属子流

平面地图是一个强大的操作员,在很多情况下都有帮助。例如,给定一个返回可流动的,我们想用第一个服务发出的值调用另一个服务:

易流动%3C存货%3E库存来源=仓库。getInventoryasync();清单资源。平面图(清单)-%3E企业资源计划。getDemandAsync(清单)。getId())。地图(需求-%3E "项目" +清单项目。getName()+ "有需求" +需求)。订阅(系统。在外::println);

延续

有时,当一个项目变得可用时,人们希望对它执行一些相关的计算。这有时被称为延续并且,根据应该发生的事情和涉及的类型,可能需要各种操作员来完成。

依靠的

最典型的场景是给定一个值,调用另一个服务,等待并继续其结果:

服务。apiCall()。平面图(值-%3E服务。另一个调用(值)。平面图(下一个-%3E服务。最终呼叫(下一个))

通常情况下,后面的序列需要来自前面映射的值。这可以通过移动外部平面地图进入到之前的内部平面地图例如:

服务。apiCall()。平面图(值-%3E服务。另一个调用(值)。平面图(下一个-%3E服务。最后调用两者(值,下一步))

这里,原件价值将在内部提供平面地图,由lambda变量捕获提供。

不依赖

在其他情况下,第一个源/数据流的结果是不相关的,人们希望继续使用准独立的另一个源。这里,平面地图同样有效:

可观察量继续的=sourceObservable。平面映射单(忽略-%3E一些单一来源)继续。地图(v-%3Ev。toString())。订阅(系统。在外::println,可投掷的::printstackTrace);

然而,这种情况会继续下去可观察量而不是更合适的单身。(这是可以理解的,因为从平面地图单个,sourceObservable是多值源,因此映射也可以产生多个值)。

虽然通常有一种方法通过使用可完成的作为调停者和它的操作者然后用别的东西继续:

sourceObservable。忽略选举()//返回可完成的。然后(某个单一来源)。地图(v-%3Ev。toString())

之间唯一的依赖关系sourceObservable还有某个单一来源前者应该正常完成,以便后者被消费。

延迟依赖型

有时,在前一个序列和新序列之间存在隐含的数据依赖关系,由于某种原因,新序列没有流过“常规通道”。人们倾向于写如下这样的续篇:

原子集成器数数= 新建 原子集成器();可观察量。范围(1,10)。下一步(忽略-%3E数数。增量和获取())。忽略选举()。然后(单身。只是(数一数。get()))。订阅(系统。在外::println);

不幸的是,这是指纹0因为Single.just(count.get())评估时间为装配时间当数据流还没有运行时。我们需要推迟评估的东西单身来源直到运行时间当主信号源完成时:

原子集成器数数= 新建 原子集成器();可观察量。范围(1,10)。下一步(忽略-%3E数数。增量和获取())。忽略选举()。然后(单身。延期(()-%3E 单身。只是(数一数。get())))。订阅(系统。在外::println);

或者

原子集成器数数= 新建 原子集成器();可观察量。范围(1,10)。下一步(忽略-%3E数数。增量和获取())。忽略选举()。然后(单身。fromCallable(()-%3E数数。get()))。订阅(系统。在外::println);

类型转换

有时,一个源或服务返回的类型不同于应该与之一起工作的流。例如,在上面的库存示例中,getDemandAsync可以返回一个单一% 3需求记录%3E。如果代码示例保持不变,这将导致编译时错误(但是,通常带有关于缺少重载的误导性错误消息)。

在这种情况下,通常有两个选项来修复转换:1)转换为所需类型,或者2)查找并使用支持不同类型的特定运算符的重载。

转换为所需类型

每个反应性基类都有能够执行这种转换(包括协议转换)以匹配其他类型的运算符。下表显示了可用的转换选项:

7f91b43275e69d76b1d71ae350b03598.png

1:当将多值源转换为单值源时,应该决定将多个源值中的哪一个视为结果。

2:转动可观察量变成可流动的需要一个额外的决定:如何处理潜在的不受约束的源流可观察量?有几种策略可用(如缓冲、丢弃、保持最新)背压策略参数或通过标准可流动的操作员,例如onBackpressureBuffer,onBackpressureDrop,onBackpressureLatest最新版这也允许背压行为的进一步定制。

3:当只有(最多)一个源项目时,背压没有问题,因为它可以一直存储,直到下游准备消耗。

使用所需类型的重载

许多经常使用的运算符都有可以处理其他类型的重载。这些通常以目标类型的后缀命名:

83b92d74831d7c2e6b1b2da30a58be44.png

这些运算符有后缀而不是简单地用不同的签名取相同的名称的原因是类型擦除。Java不考虑签名,例如操作员(功能%3CT,单一%3CR%3E%3E)和操作员(功能%3CT,可能%3CR%3E%3E)不同(不像C#)并且由于擦除,两者操作员s最终会成为具有相同签名的重复方法。

操作员命名约定

编程中的命名是最难的事情之一,因为名字应该不会太长,表达性强,容易捕捉和记忆。不幸的是,目标语言(和预先存在的约定)在这方面可能不会提供太多帮助(不可用的关键字、类型擦除、类型歧义等)。)。

不可用的关键字

在最初的Rx.NET,发出单个项目然后完成的操作符被调用返回(T)。因为Java惯例是用小写字母开始一个方法名,所以应该是返回(T)这是Java中的一个关键字,因此不可用。因此,RxJava选择命名这个运算符只是。操作员也有同样的限制转换,必须命名开关连接。还有一个例子是捕捉它被命名为一个错误摘要。

类型擦除

许多期望用户提供一些返回反应类型的函数的运算符不能重载,因为函数%3CT,X%3E将此类方法签名转换为副本。RxJava选择通过附加类型作为后缀来命名这样的运算符:

易流动%3Cr%3E平面地图(功能%3C?极好的T,?延伸出版商%3C?延伸r%3E%3E映射器)易流动%3Cr%3E平面图也许(功能%3C?极好的T,?延伸可能来源%3C?延伸r%3E%3E映射器)

类型歧义

即使某些操作符没有类型擦除的问题,它们的签名可能会变得模糊不清,尤其是当使用Java 8和lambdas时。例如,有几个重载串联将各种其他反应性基本类型作为参数(为底层实现提供便利和性能优势):

易流动%3CT%3E串联(出版商%3C?延伸T%3E其他);易流动%3CT%3E串联(单源%3C?延伸T%3E其他);

两者出版者和SingleSource显示为功能接口(使用一种抽象方法的类型),可能会鼓励用户尝试提供lambda表达式:

someSource。串联-%3E 单身。只是(2))。订阅(系统。在外::println,可投掷的::printstackTrace);

不幸的是,这种方法行不通,这个例子也不能打印出来2一点也不。事实上,自2.1.10版以来,它甚至无法编译,因为至少4串联重载存在,编译器发现上面的代码不明确。

在这种情况下,用户可能希望推迟一些计算,直到someSource已经完成,因此正确的明确运算符应该是推迟以下内容:

someSource。串联(单身。延期(()-%3E 单身。只是(2))。订阅(系统。在外::println,可投掷的::printstackTrace);

有时,添加后缀是为了避免可能编译但在流中产生错误类型的逻辑歧义:

易流动%3CT%3E合并(出版商%3C?延伸出版商%3C?延伸T%3E%3E来源);易流动%3CT%3E合并数组(出版商%3C?延伸T%3E...来源);

当函数接口类型作为类型参数参与进来时,这也会变得模糊不清T。

错误处理

数据流可能会失败,此时错误会被发送给消费者。然而,有时多个源可能会失败,此时可以选择是等待所有源完成还是失败。为了表示这个机会,许多运算符名称都以延迟错误单词(而其他单词以a为特征延迟错误或者delayErrors其中一个重载中的布尔标志):

易流动%3CT%3Econcat(出版商%3C?延伸出版商%3C?延伸T%3E%3E来源);易流动%3CT%3E串联错误(出版商%3C?延伸出版商%3C?延伸T%3E%3E来源);

当然,各种后缀可能会一起出现:

易流动%3CT%3EconcatArrayEagerDelayError(出版商%3C?延伸T%3E...来源);

基类与基类

由于基类上静态和实例方法的绝对数量,基类可以被认为很重。RxJava 2的设计受到了反应物流因此,规范中,库为每种反应类型提供了一个类和一个接口:

00c6140c975428835ee446b15ddef3ef.png

1这org.reactivestreams.Publisher是外部反应流库的一部分。它是通过由反应物流规范。

2接口的命名约定是追加来源半传统的类名。没有FlowableSource因为出版者是由反应流库提供的(并且对其进行细分也无助于互操作)。然而,从反应流规范的意义上来说,这些接口不是标准的,并且目前仅针对RxJava。

沟通

  • 谷歌集团:RxJava
  • Twitter:@RxJava
  • 数字枢纽问题
  • StackOverflow:rx-java和rx-java2
  • Gitter.im

版本控制

版本2.x现在被认为是稳定的和最终的。1.x版和2.x版将支持数年。增强和错误修复将在两者之间及时同步。

当添加了重要的新功能,或者出现了可能会影响某些边缘情况(例如依赖于bug导致的行为)的行为变化的重大增强或bug修复时,会出现较小的2.x增量(例如2.1、2.2等)。可以归类为这种增强的一个例子是向以前不支持的操作员添加反作用拉背压支持。这应该向后兼容,但行为不同。

修补程序2.x.y增量(如2.0.0 -%3E 2.0.1,2.3.1 -%3E 2.3.2等)将用于错误修复和琐碎功能(如添加方法重载)。用标记的新功能@贝塔或者@实验还可以在补丁发布中添加注释,以允许快速探索和迭代不稳定的新功能。

@贝塔

标有@贝塔类或方法级别的注释可能会发生变化。它们可以以任何方式修改,甚至可以随时删除。如果您的代码本身是一个库(即它在您自己控制之外的用户的类路径上使用),您不应该使用beta APIs,除非您重新打包它们(例如使用ProGuard、shading等)。

@实验

标有@实验类或方法级别的注释几乎肯定会改变。它们可以以任何方式修改,甚至可以随时删除。您不应该在任何生产代码中使用或依赖它们。它们纯粹是为了允许广泛的测试和反馈。

@已弃用

标有@已弃用在下一个主要版本之前,类或方法级别的注释将保持受支持,但是建议停止使用它们。

io.reactivex.internal.*

中的所有代码io.reactivex.internal.*包被认为是私有的应用编程接口,根本不应该被依赖。它可以随时改变。

完整文档

  • 维基
  • Javadoc
  • 最新snaphot Javadoc
  • 特定的Javadoc发布版本以下内容:http://reactivex.io/RxJava/2.x/javadoc/2.x.y/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值