提交 9016e112 编写于 作者: W wizardforcel

2021-10-12 21:34:07

上级 0cc68459
# 序言
# 零、序言
反应式编程已经存在了几十年。从 Smalltalk 还是一种年轻的语言时起,就有一些反应式编程的实现。然而,它只是最近才流行起来,现在正成为一种趋势。你为什么现在问?因为它有助于编写快速、实时的应用程序,而当前的技术和 Web 都需要它。
反应式编程已经存在了几十年。从 Smalltalk 还是一种年轻的语言时起,就有一些反应式编程的实现。然而,它只是最近才流行起来,现在正成为一种趋势。你为什么现在问?因为它有助于编写快速、实时的应用,而当前的技术和 Web 都需要它。
我早在 2008 年就参与了这项工作,当时我所在的团队正在开发一个名为 Sophie 2 的多媒体图书创作者。它必须快速且响应迅速,因此我们创建了一个名为 Prolib 的框架,该框架为对象提供了相互依赖的属性(换句话说,我们实现了 Swing 和更多转换、过滤等的绑定)。像这样将模型数据连接到 GUI 感觉很自然。
......@@ -22,9 +22,9 @@
[第 6 章](6.html "Chapter 6. Using Concurrency and Parallelism with Schedulers")*使用与调度器的并发和并行*将指导您完成使用 RxJava 编写并发和并行程序的过程。这将由 RxJava 调度器完成。将介绍调度器的类型,您将了解何时以及为什么使用每种调度器。本章将向您介绍如何避免和施加背压的机制。
[第 7 章](7.html "Chapter 7. Testing Your RxJava Application")*测试 RxJava 应用程序*将向您展示如何对 RxJava 应用程序进行单元测试。
[第 7 章](7.html "Chapter 7. Testing Your RxJava Application")*测试 RxJava 应用*将向您展示如何对 RxJava 应用进行单元测试。
[第 8 章](8.html "Chapter 8. Resource Management and Extending RxJava")*资源管理和扩展 RxJava*将教您如何管理 RxJava 应用程序用作数据源的资源。我们将在这里写下我们自己的可观测算符。
[第 8 章](8.html "Chapter 8. Resource Management and Extending RxJava")*资源管理和扩展 RxJava*将教您如何管理 RxJava 应用用作数据源的资源。我们将在这里写下我们自己的可观测算符。
# 这本书你需要什么
......
# 第一章反应式编程简介
# 一、反应式编程简介
如今,术语**反应式编程**正在成为一种趋势。各种编程语言的库和框架正在涌现。关于反应式编程的博客文章、文章和演示正在创建中。Facebook、SoundCloud、Microsoft 和 Netflix 等大公司都在支持和使用这一概念。因此,作为程序员,我们开始对此感到好奇。为什么人们对反应式编程如此兴奋?反应性是什么意思?这对我们的项目有帮助吗?我们应该学习如何使用它吗?
同时,Java 以其多线程、速度、可靠性和良好的可移植性而广受欢迎。它用于构建各种各样的应用程序,从搜索引擎到数据库,再到运行在服务器集群上的复杂 web 应用程序。但是 Java 的名声也不好,仅使用内置工具很难编写并发和简单的应用程序,用 Java 编程需要编写大量样板代码。此外,如果您需要异步(例如使用 futures),您可以很容易地进入“回调地狱”,这实际上适用于所有编程语言。
同时,Java 以其多线程、速度、可靠性和良好的可移植性而广受欢迎。它用于构建各种各样的应用,从搜索引擎到数据库,再到运行在服务器集群上的复杂 web 应用。但是 Java 的名声也不好,仅使用内置工具很难编写并发和简单的应用,用 Java 编程需要编写大量样板代码。此外,如果您需要异步(例如使用 futures),您可以很容易地进入“回调地狱”,这实际上适用于所有编程语言。
换句话说,Java 是强大的,您可以用它创建伟大的应用程序,但这并不容易。好消息是,有一种方法可以改变这一点,即使用反应式编程风格。
换句话说,Java 是强大的,您可以用它创建伟大的应用,但这并不容易。好消息是,有一种方法可以改变这一点,即使用反应式编程风格。
本书将呈现**RxJava**[https://github.com/ReactiveX/RxJava](https://github.com/ReactiveX/RxJava) ),反应式编程范式的开源 Java 实现。使用 RxJava 编写代码需要一种不同的思维方式,但它将使您能够使用结构良好的简单代码片段创建复杂的逻辑。
......@@ -49,17 +49,17 @@ System.out.println(c);
# 我们为什么要被动?
对于我们来说,回答这个问题最简单的方法就是思考一下我们现在在构建应用程序时的需求。
对于我们来说,回答这个问题最简单的方法就是思考一下我们现在在构建应用时的需求。
虽然 10-15 年前网站进行维护或响应速度慢是正常的,但今天所有东西都应该全天候在线,并以闪电般的速度响应;如果速度慢或慢,用户会更喜欢其他服务。今天,慢意味着无法使用或损坏。我们正在处理大量数据,这些数据需要我们快速服务和处理。
HTTP 失败在最近的过去并不罕见,但现在,我们必须具备容错能力,为用户提供可读且合理的消息更新。
在过去,我们编写简单的桌面应用程序,但今天我们编写 web 应用程序,它应该快速响应。在大多数情况下,这些应用程序与大量远程服务通信。
在过去,我们编写简单的桌面应用,但今天我们编写 web 应用,它应该快速响应。在大多数情况下,这些应用与大量远程服务通信。
如果我们希望我们的软件具有竞争力,这些是我们必须满足的新要求。换句话说,我们必须:
* 模块化/动态:通过这种方式,我们将能够拥有 24/7 系统,因为模块可以离线和在线,而不会破坏或停止整个系统。此外,这有助于我们在应用程序变大和管理其代码库时更好地构建应用程序
* 模块化/动态:通过这种方式,我们将能够拥有 24/7 系统,因为模块可以离线和在线,而不会破坏或停止整个系统。此外,这有助于我们在应用变大和管理其代码库时更好地构建应用
* 可伸缩性:通过这种方式,我们将能够处理大量数据或大量用户请求。
* 容错:通过这种方式,系统将对其用户保持稳定。
* 响应性:这意味着快速可用。
......@@ -71,15 +71,15 @@ HTTP 失败在最近的过去并不罕见,但现在,我们必须具备容错
* 对故障/错误作出反应将使系统更具容错性。
* 响应意味着及时响应用户活动。
如果应用程序是事件驱动的,则可以将其解耦为多个独立组件。这有助于我们变得更具可伸缩性,因为我们始终可以添加新组件或删除旧组件,而不会停止或破坏系统。如果将错误和故障传递给正确的组件(该组件可以将其作为通知处理),则应用程序可以变得更具容错性或弹性。因此,如果我们将系统构建为事件驱动,我们可以更轻松地实现可伸缩性和容错性,并且一个可伸缩、解耦和防错的应用程序能够快速响应用户。
如果应用是事件驱动的,则可以将其解耦为多个独立组件。这有助于我们变得更具可伸缩性,因为我们始终可以添加新组件或删除旧组件,而不会停止或破坏系统。如果将错误和故障传递给正确的组件(该组件可以将其作为通知处理),则应用可以变得更具容错性或弹性。因此,如果我们将系统构建为事件驱动,我们可以更轻松地实现可伸缩性和容错性,并且一个可伸缩、解耦和防错的应用能够快速响应用户。
![Why should we be reactive?](img/4305_01_01.jpg)
**反应性宣言**[http://www.reactivemanifesto.org/](http://www.reactivemanifesto.org/) 是定义我们前面提到的四个反应性原则的文件。每个反应式系统应为消息驱动(事件驱动)。通过这种方式,它可以变得松散耦合,因此具有可伸缩性和弹性(容错),这意味着它是可靠和响应性的(参见上图)。
请注意,《反应式宣言》描述的是反应式系统,与我们对反应式编程的定义不同。您可以构建一个消息驱动、有弹性、可扩展且响应迅速的应用程序,而无需使用反应式库或语言。
请注意,《反应式宣言》描述的是反应式系统,与我们对反应式编程的定义不同。您可以构建一个消息驱动、有弹性、可扩展且响应迅速的应用,而无需使用反应式库或语言。
应用程序数据中的更改可以用通知建模,通知可以传播到正确的处理程序。因此,使用反应式编程编写应用程序是遵守宣言的最简单方法。
应用数据中的更改可以用通知建模,通知可以传播到正确的处理程序。因此,使用反应式编程编写应用是遵守宣言的最简单方法。
# 介绍 RxJava
......@@ -221,7 +221,7 @@ new Action0() {
使用这种流进行编码是一种更具功能性的反应式编程实现。当然,它有正式的定义和复杂的术语,但这是最简单的解释。
订阅活动应该熟悉;例如,单击 GUI 应用程序中的按钮将触发一个事件,该事件将传播到订阅服务器处理程序。但是,使用 RxJava,我们可以从任何文件输入、套接字、响应、变量、缓存、用户输入等创建数据流。除此之外,还可以通知消费者流已关闭,或者出现错误。因此,通过使用这些流,我们的应用程序可以对故障做出反应。
订阅活动应该熟悉;例如,单击 GUI 应用中的按钮将触发一个事件,该事件将传播到订阅服务器处理程序。但是,使用 RxJava,我们可以从任何文件输入、套接字、响应、变量、缓存、用户输入等创建数据流。除此之外,还可以通知消费者流已关闭,或者出现错误。因此,通过使用这些流,我们的应用可以对故障做出反应。
总而言之,流是一系列正在进行的消息/事件,在实时处理时按顺序排列。它可以被视为一个随时间变化的值,订阅者(消费者)可以根据它观察到这些变化。因此,回到 Excel 中的示例,我们已经用“反应变量”或 RxJava 的`Observable`实例有效地替换了传统变量。** **## 实现无功和
......@@ -229,7 +229,7 @@ new Action0() {
让我们看看我们的计划必须满足的要求:
* 它将是在终端中运行的应用程序
* 它将是在终端中运行的应用。
* 一旦启动,它将一直运行,直到用户输入`exit`
* 如果用户输入`a:<number>`,则*a*收集器将更新为*<编号>*
* 如果用户输入`b:<number>`,则*b*收集器将更新为*<编号>*
......@@ -394,7 +394,7 @@ update : a + b = 11.0
这个示例的源代码可以从这里下载并试用:[https://github.com/meddle0x53/learning-rxjava/blob/master/src/main/java/com/packtpub/reactive/chapter01/ReactiveSumV1.java](https://github.com/meddle0x53/learning-rxjava/blob/master/src/main/java/com/packtpub/reactive/chapter01/ReactiveSumV1.java)** **# 总结
在本章中,我们介绍了反应式原则以及我们应该学习和使用它们的原因。构建一个反应式应用程序并不难;它只需要在一些声明性的步骤中构造程序。使用 RxJava,可以通过构建多个以正确方式连接的异步流来实现这一点,并通过其使用者一路转换数据。
在本章中,我们介绍了反应式原则以及我们应该学习和使用它们的原因。构建一个反应式应用并不难;它只需要在一些声明性的步骤中构造程序。使用 RxJava,可以通过构建多个以正确方式连接的异步流来实现这一点,并通过其使用者一路转换数据。
本章中介绍的两个示例乍看起来可能有点复杂和混乱,但实际上,它们非常简单。其中有很多新的东西,但所有的东西都将在下面的章节中详细解释。
......
# 第二章,使用 Java 8 的函数结构
# 二、使用 Java 8 的函数结构
函数式编程不是一个新概念;事实上,它很旧了。例如,**Lisp**是一种函数式语言,是当今第二古老的常用编程语言。
......@@ -6,7 +6,7 @@
有了 Java8,Java 世界获得了 lambda 表达式和将函数传递给函数的能力。有了它们,我们可以用一种更实用的方式编写代码,并摆脱大量的样板代码。我们在 Java8 中得到的另一个新东西是流,它非常类似于 RxJava 的可观察对象,但不是异步的。结合这些流和 lambda,我们能够创建更具功能性的程序。
我们将熟悉这些新结构,并了解如何将它们用于 RxJava 的抽象。通过使用 lambdas,我们的程序将更简单、更容易理解,本章介绍的概念将有助于设计应用程序
我们将熟悉这些新结构,并了解如何将它们用于 RxJava 的抽象。通过使用 lambdas,我们的程序将更简单、更容易理解,本章介绍的概念将有助于设计应用。
本章包括:
......@@ -288,7 +288,7 @@ public static void reactiveSum(
你不必记住本章介绍的大部分术语;重要的是要理解它们是如何帮助我们编写简单但功能强大的程序的。
RxJava 的方法包含了许多功能性思想,因此,学习如何以更功能性的方式思考,以便编写更好的反应式应用程序,对我们来说非常重要。
RxJava 的方法包含了许多功能性思想,因此,学习如何以更功能性的方式思考,以便编写更好的反应式应用,对我们来说非常重要。
## 纯函数
......
# 第三章:创建和连接观察对象、观察者和主体
# 三、创建和连接可观察对象、观察者和主体
RxJava 的`Observable`实例是反应式应用程序的构建块,RxJava 的这一优势是有益的。如果我们有一个源`Observable`实例,我们可以将逻辑链接到它并*订阅*以获得结果。我们只需要这个初始`Observable`实例。
RxJava 的`Observable`实例是反应式应用的构建块,RxJava 的这一优势是有益的。如果我们有一个源`Observable`实例,我们可以将逻辑链接到它并*订阅*以获得结果。我们只需要这个初始`Observable`实例。
在浏览器或桌面应用程序中,用户输入已经由我们可以通过`Observable`实例处理和转发的事件表示。但是,将我们所有的数据更改或操作转换为`Observable`实例,而不仅仅是用户输入,这将是非常棒的。例如,当我们从一个文件中读取数据时,将读取的每一行或每一个字节序列看作是一条可以通过`Observable`实例发出的消息,这将是一件好事。
在浏览器或桌面应用中,用户输入已经由我们可以通过`Observable`实例处理和转发的事件表示。但是,将我们所有的数据更改或操作转换为`Observable`实例,而不仅仅是用户输入,这将是非常棒的。例如,当我们从一个文件中读取数据时,将读取的每一行或每一个字节序列看作是一条可以通过`Observable`实例发出的消息,这将是一件好事。
我们将详细介绍如何将不同的数据源转换为`Observable`实例;不管它们是外部(文件或用户输入)还是内部(集合或标量)。此外,我们还将了解各种类型的`Observable`实例,具体取决于它们的行为。我们将学习的另一个重要内容是如何以及何时取消订阅`Observable`实例,以及如何使用订阅和`Observer`实例。此外,我们还将介绍主题类型及其用法。
......@@ -554,7 +554,7 @@ Sum : 9.0
上例的源代码可在[查看/下载 https://github.com/meddle0x53/learning-rxjava/blob/master/src/main/java/com/packtpub/reactive/chapter03/ReactiveSumV3.java](https://github.com/meddle0x53/learning-rxjava/blob/master/src/main/java/com/packtpub/reactive/chapter03/ReactiveSumV3.java)
*反应性属性*可以用于实现绑定和计数器,因此它们对于桌面或浏览器应用程序非常有用。但这个例子与任何功能范式都相去甚远。
*反应性属性*可以用于实现绑定和计数器,因此它们对于桌面或浏览器应用非常有用。但这个例子与任何功能范式都相去甚远。
# 总结
......
# 第四章。转换、过滤和积累您的数据
# 四、转换、过滤和积累您的数据
现在我们有了从各种源数据创建`Observable`实例的方法,是时候围绕这些实例构建编程逻辑了。我们将介绍用于实现逐步计算的基本反应运算符(处理数据的反应方式)。
......
# 第 5 章组合词、条件和错误处理
# 五、组合器、条件和错误处理
我们编写的大多数程序都处理来自不同来源的数据。这些源既可以是外部的(文件、数据库、服务器等),也可以是内部的(同一外部源的不同集合或分支)。在许多情况下,我们希望这些资源以某种方式相互依赖。定义这些依赖关系是构建程序的必要步骤。本章的目的是介绍能够做到这一点的`Observable`操作符。
......@@ -257,7 +257,7 @@ subscribePrint(words.skipUntil(interval), "skipUntil"); // (4)
3. 这里,`takeWhile()`操作员在`words Observable`上设置一个条件。仅当存在包含两个以上字母的单词时,它才会发出。因为“`or'`有两个字母,所以它不会被释放,并且它后面的所有单词也会被跳过。`takeUntil()`操作符也有类似的重载,但它只发出包含少于三个字母的单词。没有`takeWhile(Observable)`操作符过载,因为它本质上是`zip()`操作符:仅当另一个操作符也发射时才发射。
4. `skip*`运算符与`take*`运算符类似。不同之处在于,它们在满足条件之前/期间不会发射。在此示例中,跳过单词`one``way`,因为它们在订阅的第 500 毫秒之前发出,`interval Observable`在第 500 毫秒开始发出。单词`'or'`及其后面的所有单词都会发出。
例如,这些*条件*操作符可用于在 GUI 应用程序中显示加载动画。代码可以是类似的东西:
例如,这些*条件*操作符可用于在 GUI 应用中显示加载动画。代码可以是类似的东西:
```java
loadingAnimationObservable.takeUntil(requestObservable);
......@@ -284,7 +284,7 @@ subscribePrint(test, "defaultIfEmpty");
`amb()``take*``skip*``defaultIfEmpty()`操作符示例的源代码可在[中找到 https://github.com/meddle0x53/learning-rxjava/blob/master/src/main/java/com/packtpub/reactive/chapter05/Conditionals.java](https://github.com/meddle0x53/learning-rxjava/blob/master/src/main/java/com/packtpub/reactive/chapter05/Conditionals.java)
到目前为止,我们已经对数据进行了转换、过滤和组合。但是*错误*呢?我们的应用程序可以随时进入错误状态。是的,我们可以订阅`Observable`实例发出的*错误*,但这将终止我们的逻辑。在`subscribe`方法中,我们在`Observable`操作链之外。如果我们想要对`Observable`实例链中的*错误*做出反应并试图阻止终止,该怎么办?有一些操作符可以帮助我们做到这一点,我们将在下一节中对它们进行研究。
到目前为止,我们已经对数据进行了转换、过滤和组合。但是*错误*呢?我们的应用可以随时进入错误状态。是的,我们可以订阅`Observable`实例发出的*错误*,但这将终止我们的逻辑。在`subscribe`方法中,我们在`Observable`操作链之外。如果我们想要对`Observable`实例链中的*错误*做出反应并试图阻止终止,该怎么办?有一些操作符可以帮助我们做到这一点,我们将在下一节中对它们进行研究。
# 处理错误
......@@ -339,7 +339,7 @@ Observable<String> numbers = Observable
## 重试技术
重试是一项重要的技术。当`Observable`实例从不确定的来源(例如,远程服务器)发送数据时,一个网络问题可能会终止整个应用程序。在*错误*上重试可以在这种情况下节省我们的时间。
重试是一项重要的技术。当`Observable`实例从不确定的来源(例如,远程服务器)发送数据时,一个网络问题可能会终止整个应用。在*错误*上重试可以在这种情况下节省我们的时间。
`Observable`动作链中插入`retry()`操作符意味着,如果发生*错误*,订阅者将重新订阅源`Observable`实例,从链的开始就尝试一切。如果再次出现*错误*,则重新启动所有程序。无参数的`retry()`操作符无限次重试。有一个重载`retry(int)`方法,它获取允许的最大重试次数。
......
# 第 6 章:与调度器使用并发性和并行性
# 六、使用调度器获得并发性和并行性
现代处理器有多个核,可以更快地同时处理许多耗时的操作。Java 并发 API(包括线程和更多)使这一点成为可能。
......@@ -651,7 +651,7 @@ RxComputationThreadPool-3|Chars : --->|
如果我们将操作符向上移动`Observable`链,则大部分逻辑将使用*计算*调度器执行。
当然,`observeOn()`操作符可以与`subscribeOn()`操作符一起使用。这样,链的一部分可以在一个线程上执行,其余部分可以在另一个线程上执行(在大多数情况下)。这在编写客户端应用程序代码时特别有用,因为这些应用程序通常在一个*事件排队*线程上运行。您可以使用带有`subscribeOn()`/`observeOn()`操作符的*IO*调度程序读取文件/服务器,然后在*事件*线程上观察结果。
当然,`observeOn()`操作符可以与`subscribeOn()`操作符一起使用。这样,链的一部分可以在一个线程上执行,其余部分可以在另一个线程上执行(在大多数情况下)。这在编写客户端应用代码时特别有用,因为这些应用通常在一个*事件排队*线程上运行。您可以使用带有`subscribeOn()`/`observeOn()`操作符的*IO*调度程序读取文件/服务器,然后在*事件*线程上观察结果。
### 提示
......@@ -979,4 +979,4 @@ Helpers.subscribePrint(
有了这些,我们就有了编写任意 RxJava 程序的知识,能够处理来自不同来源的数据。我们知道如何使用多个线程来实现这一点。使用 RxJava 及其运算符和*结构*几乎就像使用新语言进行编码一样。它有自己的规则和流量控制方法。
为了编写稳定的应用程序,我们必须学习如何*单元测试*它们。测试*异步*代码并非易事。好消息是,RxJava 提供的一些操作符和类将帮助我们做到这一点。你可以在下一章中阅读更多关于它们的内容。
\ No newline at end of file
为了编写稳定的应用,我们必须学习如何*单元测试*它们。测试*异步*代码并非易事。好消息是,RxJava 提供的一些操作符和类将帮助我们做到这一点。你可以在下一章中阅读更多关于它们的内容。
\ No newline at end of file
# 第 7 章:测试 RxJava 应用程序
# 七、测试 RxJava 应用
在编写软件时,尤其是编写将被许多用户使用的软件时,我们需要确保一切都正常工作。我们可以编写可读、结构良好、模块化的代码,这将使更改和维护更容易。我们应该编写测试,因为每个特性都有回归的危险。当我们已经对现有代码进行了测试时,重构就不会那么困难了,因为测试可以针对新的、更改过的代码运行。
......
# 第 8 章资源管理与 RxJava 扩展
# 八、资源管理与 RxJava 扩展
通过前面的章节,我们已经学习了如何使用 RxJava 的可观察性。我们一直在使用许多不同的运算符和`factory`方法。`factory`方法是各种`Observable`实例的来源,具有不同的行为和排放源。另一方面,通过使用操作符,我们已经围绕这些观测值构建了复杂的逻辑。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册