第3章 基本运算符

第3章 基本运算符

导读

在上一章中,您了解了很多有关Observable和Observer的知识。 我们还介绍了少数运算符,尤其是map()和filter(),以了解运算符的作用。 但是我们可以利用数百个RxJava运算符来表达业务逻辑和行为。 通过本书的大部分内容,我们将全面介绍运算符,因此您知道使用哪些以及何时使用。 了解可用的运算符并将其合并对于成功使用ReactiveX至关重要。 您应该努力使用运算符来表达业务逻辑,以使您的代码保持尽可能的被动。

应该注意的是,他们是被自己的Observable的观察者调用的。 如果在Observable上调用map(),则返回的Observable将对其进行订阅。 然后它将转换每个发射,并依次成为下游观察者的创建者,包括其他和终端观察者本身。

您应该努力使用RxJava运算符来执行尽可能多的逻辑,并且应该使用观察者来接收可以汇总的最终发射量。 尽量不要通过从Observable链中提取值来欺骗或发挥创造力,或者尝试使用阻塞过程或命令式编程策略。 当您使算法和过程保持响应式时,您可以轻松利用响应式编程的好处,例如降低内存使用量,灵活的并发性和可处置性。

在本章中,我们将介绍以下主题:

抑制运算符

转型运算符

减少运算符

错误恢复运算符

动作运算符

一、抑制运算符

有许多运算符将抑制不符合指定标准的发射。 这些运算符的工作原理是不对不合格的发射不调用下游的onNext()函数,因此不会进入观察者链。 我们已经看到了filter()运算符,它可能是最常见的抑制运算符。 我们将从这个开始。

1.1、filter()

filter()运算符接受给定Observable<T>的Predicate<T>。 这意味着您需要为它提供一个lambda,以通过将其映射到布尔值来限定每个发射,并且带有false的发射将不会继续。

例如,可以使用filter()仅允许发射长度不超过五个字符的字符串:

import io.reactivex.Observable;

public class Ch3_1 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta", "Epsilon")
                .filter(s -> s.length() != 5)
                .subscribe(s -> System.out.println("RECEIVED: " + s));
    }
}

输出结果如下:

RECEIVED: Beta

RECEIVED: Epsilon

filter()函数可能是抑制发射的最常用运算符。

请注意,如果所有发射均不符合您的条件,则返回的Observable将为空,在调用onComplete()之前不会发生任何发射

1.2、take()

take()运算符有两个重载。 一个将获取指定数量的发射,然后在捕获所有发射后调用onComplete()。 它还将处理整个订阅,因此不会发生更多的发射。

例如,take(3)将发出前三个发射,然后调用onComplete()事件:

import io.reactivex.Observable;

public class Ch3_2 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta", "Epsilon")
                .take(3)
                .subscribe(s -> System.out.println("RECEIVED: " + s));
    }
}

输出结果如下:

RECEIVED: Alpha
RECEIVED: Beta
RECEIVED: Gamma

请注意,如果收到的发射量少于take()函数中指定的发射量,它将仅发射其实际获得的发射,然后调用onComplete()函数。

另一个重载将在特定的持续时间内进行发射,然后调用onComplete()。 当然,这里的冷Observable发射得如此之快,以至于在这种情况下将是一个不好的例子。 也许更好的例子是使用Observable.interval()函数。 让我们每300毫秒发射一次,但在以下代码片段中,take()发射仅持续2秒钟:

import io.reactivex.Observable;

import java.util.concurrent.TimeUnit;

public class Ch3_3 {
    public static void main(String[] args) {
        Observable.interval(300, TimeUnit.MILLISECONDS)
                .take(2, TimeUnit.SECONDS)
                .subscribe(i -> System.out.println("RECEIVED: " + i));
        sleep(5000);
    }

    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出结果如下:
RECEIVED: 0

RECEIVED: 1

RECEIVED: 2

RECEIVED: 3

RECEIVED: 4

RECEIVED: 5

您可能会获得此处显示的输出(每300毫秒打印一次)。 如果它们间隔300毫秒,那么您只能在2秒内获得六次发射

请注意,还有一个takeLast()运算符,它将在调用onComplete()函数之前采用最后指定的发射数量(或持续时间)。 只需记住,它将在内部将发射排入队列,直到调用其onComplete()函数为止,然后它可以在逻辑上标识并发射最后一个发射。

1.3、skip()

skip()运算符与take()运算符相反。 它将忽略指定的发射量,然后发射随后的发射量。

如果要跳过Observable的前90个发射,可以使用此运算符,如以下代码片段所示:

import io.reactivex.Observable;

public class Ch3_4 {
    public static void main(String[] args) {
        Observable.range(1, 100)
                .skip(90)
                .subscribe(i -> System.out.println("RECEIVED: " + i));
    }
}

输出结果如下:
RECEIVED: 91

RECEIVED: 92

RECEIVED: 93

RECEIVED: 94

RECEIVED: 95

RECEIVED: 96

RECEIVED: 97

RECEIVED: 98

RECEIVED: 99

RECEIVED: 100

就像take()运算符一样,也有一个接受持续时间的重载。 还有一个skipLast()运算符,它将在调用onComplete()事件之前跳过最后指定的项目数(或时间)。 请记住,skipLast()操作符将排队并延迟发射,直到确认该范围内的最后发射为止。

1.4、takeWhile()和skipWhile()

take()运算符的另一个变体是takeWhile()运算符,该运算符在从每次发射派生的条件为true时进行发射。

下面的示例将保持发射小于5的发射。遇到不存在的发射时,它将调用onComplete()函数并对其进行处理:

import io.reactivex.Observable;

public class Ch3_5 {
    public static void main(String[] args) {
        Observable.range(1, 100)
                .takeWhile(i -> i < 5)
                .subscribe(i -> System.out.println("RECEIVED: " + i));
    }
}

输出结果如下:

RECEIVED: 1

RECEIVED: 2

RECEIVED: 3

RECEIVED: 4

就像takeWhile()函数一样,还有一个skipWhile()函数。 在符合条件的情况下,它将继续跳过发射。 一旦条件不再合格,发射将开始通过。 在下面的代码中,只要发射小于或等于95,我们就将其跳过。遇到不符合此条件的发射时,它将允许所有后续发射继续进行:

import io.reactivex.Observable;

public class Ch3_6 {
    public static void main(String[] args) {
        Observable.range(1, 100)
                .skipWhile(i -> i <= 95)
                .subscribe(i -> System.out.println("RECEIVED: " + i));
    }
}

输出结果如下:

RECEIVED: 96

RECEIVED: 97

RECEIVED: 98

RECEIVED: 99

RECEIVED: 100

takeUntil()运算符与takeWhile()类似,但是它接受另一个Observable作为参数。 它将继续进行发射,直到其他Observable推动发射为止。skipUntil()运算符的行为类似。 它也接受另一个Observable作为参数,但是它将继续跳过,直到另一个Observable发出某些东西为止。

1.5、distinct()

distinct()运算符将发出每个唯一的发射,但它将禁止随后的任何重复。 基于所提交对象的hashCode()/ equals()判断实现。 如果我们要发出字符串序列的不同长度,则可以按如下所示进行合并:

import io.reactivex.Observable;

public class Ch3_7 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .map(String::length)
                .distinct()
                .subscribe(i -> System.out.println("RECEIVED: " + i));
    }
}

输出结果如下:

RECEIVED: 5

RECEIVED: 4

RECEIVED: 7

请记住,如果您拥有范围广泛且种类繁多的唯一值,则distinct()可能会占用一些内存。 想象一下,每个订阅都会生成一个HashSet,该HashSet可以跟踪以前捕获的唯一值。

您还可以添加一个lambda参数,该参数将每个发射映射到用于相等逻辑的键。 这使发射(而不是密钥)在将密钥用于不同逻辑时继续前进。 例如,我们可以设定每个字符串的长度并将其用于唯一性,但是发出字符串而不是它们的长度:

import io.reactivex.Observable;

public class Ch3_8 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .distinct(String::length)
                .subscribe(i -> System.out.println("RECEIVED: " + i));
    }
}

输出结果如下:

RECEIVED: Alpha

RECEIVED: Beta

RECEIVED: Epsilon

Alpha是五个字符,而Beta是四个字符。 Gamma和Delta被忽略,因为Alpha已经发出并且是5个字符。 Epsilon是七个字符,并且由于尚未发出七个字符的字符串,因此将其向前发出。

1.6、distinctUntilChanged()

differentUntilChanged()函数将忽略重复的连续发射。 这是一种忽略重复直到改变的有用方法。 如果重复发出相同的值,则所有重复项都将被忽略,直到发出新的值为止。 thenext值的重复项将被忽略,直到再次更改,依此类推。 观察以下代码的输出以查看实际的行为:

import io.reactivex.Observable;

public class Ch3_9 {
    public static void main(String[] args) {
        Observable.just(1, 1, 1, 2, 2, 3, 3, 2, 1, 1)
                .distinctUntilChanged()
                .subscribe(i -> System.out.println("RECEIVED: " + i));
    }
}

输出结果如下:

RECEIVED: 1

RECEIVED: 2

RECEIVED: 3

RECEIVED: 2

RECEIVED: 1

我们首先收到发射1,该发射被允许向前。 但是接下来的两个1被忽略,因为它们是连续的重复项。 当它切换到2时,将发出首字母2,但以下重复项将被忽略。 发出3,并且其后续副本也被忽略。 最后,我们切换回2发出,然后切换为1,其重复被忽略。

就像distinct()一样,您可以通过lambda映射为键提供可选参数。 在下面的代码片段中,我们使用按其长度键入字符串的字符串执行distinctUntilChanged()操作:

import io.reactivex.Observable;

public class Ch3_10 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Zeta", "Eta", "Gamma",
                "Delta")
                .distinctUntilChanged(String::length)
                .subscribe(i -> System.out.println("RECEIVED: " + i));
    }
}

输出结果如下:

RECEIVED: Alpha

RECEIVED: Beta

RECEIVED: Eta

RECEIVED: Gamma

请注意,Zeta被跳过是因为它紧随Beta之后也是四个字符,而Delta也被忽略了,因为它紧随也是五个字符的Gamma。

1.7、elementAt()

您可以通过由Long指定的索引(从0开始)获取特定的发射。找到并发射该项目后,将调用onComplete()并处理该预订。

如果要获取来自Observable的第四个发射,则可以执行此操作,如以下代码片段所示:

import io.reactivex.Observable;

public class Ch3_11 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Zeta", "Eta", "Gamma",
                "Delta")
                .elementAt(3)
                .subscribe(i -> System.out.println("RECEIVED: " + i));
    }
}

输出结果如下:
RECEIVED: Eta

您可能没有注意到,但是elementAt()返回Maybe<T>而不是Observable<T>。 这是因为它将产生一种发射,但是如果发射少于所需索引,则它将为空。

还有其他一些elementAt()风格,例如elementAtOrError(),它们返回Single,并且如果找不到该索引处的元素,则将发出错误。 singleElement()会将Observable转换为Maybe,但是如果一个元素之外的任何内容都会产生错误。 最后,firstElement()和lastElement()将产生,可能分别发出第一个或最后一个发射。

二、转型运算符

接下来,我们将介绍各种可转换发射的常见运算符。 可观察链中的一系列运算符是一系列转换。 您已经看过map(),它是该类别中最明显的运算符。 我们将从那个开始。

2.1、map()

对于给定的Observable<T>,map()运算符将使用提供的Function<T,R> lambda将T发射转换为R发射。 我们已经使用此运算符多次,将字符串转换为长度。 这是一个新示例:我们可以获取原始日期字符串,并使用map()运算符将每个日期字符串转换为LocalDate发射,如以下代码片段所示:

import io.reactivex.Observable;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Ch3_12 {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("M/d/yyyy");
        Observable.just("1/3/2016", "5/9/2016", "10/12/2016")
                .map(s -> LocalDate.parse(s, dtf))
                .subscribe(i -> System.out.println("RECEIVED: " + i));
    }
}

输出结果如下:

RECEIVED: 2016-01-03

RECEIVED: 2016-05-09

RECEIVED: 2016-10-12

我们传递了一个lambda,它将每个字符串转换为LocalDate对象。 我们预先创建了一个DateTimeFormatter来协助LocalDate.parse()操作,该操作返回LocalDate。 反过来,我们将每个LocalDate发射推向我们的观察者进行打印。

map()运算符对每个发射进行一对一转换。 如果需要进行一对多转换(将一种发射转换为多种发射),则可能需要使用flatMap()或concatMap(),我们将在下一章中介绍。

2.2、cast()

将每个发射转换为不同类型的简单,类似于地图的运算符为cast()。 如果要获取Observable<String>并将每个发射转换为对象(并返回Observable<Object>),则可以使用map()运算符,如下所示:

Observable<Object> items =  Observable.just("Alpha", "Beta", "Gamma")
                                      .map(s -> (Object) s);

但是我们可以使用的简写形式是cast(),我们可以简单地传递想要转换为的类类型,如以下代码片段所示:

Observable<Object> items =  Observable.just("Alpha", "Beta", "Gamma")
                                      .cast(Object.class);
2.3、startWith()

对于给定的Observable<T>,startWith()运算符允许您在所有其他发射之前插入T发射。

例如,如果我们有一个Observable<String>在要打印的菜单上发出项目,则可以使用startWith()首先添加标题标题:

import io.reactivex.Observable;

public class Ch3_13 {
    public static void main(String[] args) {
        Observable<String> menu =
                Observable.just("Coffee", "Tea", "Espresso", "Latte");
        //print menu
        menu.startWith("COFFEE SHOP MENU")
                .subscribe(System.out::println);
    }
}

输出结果如下:

COFFEE SHOP MENU

Coffee

Tea

Espresso

Latte

如果要从多个发射开始,请使用startWithArray()接受varargs参数。 如果要在标题和菜单项之间添加分隔线,则可以从标题和分隔线开始进行发射:

import io.reactivex.Observable;

public class Ch3_14 {
    public static void main(String[] args) {
        Observable<String> menu =
                Observable.just("Coffee", "Tea", "Espresso", "Latte");
        //print menu
        menu.startWithArray("COFFEE SHOP MENU", "----------------")
                .subscribe(System.out::println);
    }
}

输出结果如下:

COFFEE SHOP MENU

‘----------------’

Coffee

Tea

Espresso

Latte

startWith()运算符对于此类情况很有用,在这种情况下,我们希望播种初始值或在发射之前添加一个或多个发射。

如果您希望整个Observable的发射都在另一个Observable的发射之前,则需要使用Observable.concat()或concatWith(),我们将在下一章中介绍。

2.4、defaultIfEmpty()

如果要在给定的Observable变为空的情况下求助于单个发射,则可以使用defaultIfEmpty()。 对于给定的Observable<T>,如果在调用onComplete()时不发生任何发射,则可以指定默认的T发射。

如果我们有一个Observable<String>并过滤以Z开头的项目,但没有任何项目满足此条件,则可以求助于None:

import io.reactivex.Observable;

public class Ch3_15 {
    public static void main(String[] args) {
        Observable<String> items =
                Observable.just("Alpha", "Beta", "Gamma", "Delta", "Epsilon");
        items.filter(s -> s.startsWith("Z"))
                .defaultIfEmpty("None")
                .subscribe(System.out::println);
    }
}

输出结果如下:None

当然,如果要发生发射,我们将永远不会看到无发射。 仅当前面的Observable为空时才会发生。

2.5、switchIfEmpty()

与defaultIfEmpty()类似,switchIfEmpty()指定另一个Observable来从源Observable为空的情况下发出值。 这样,在源为空而不是仅发射一个值的情况下,您可以指定不同的发射顺序

例如,如果前面的Observable由于filter()操作而变为空,我们可以选择发出三个加迭代字符串:

import io.reactivex.Observable;

public class Ch3_16 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .filter(s -> s.startsWith("Z"))
                .switchIfEmpty(Observable.just("Zeta", "Eta", "Theta"))
                .subscribe(i -> System.out.println("RECEIVED: " + i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

输出结果如下:

RECEIVED: Zeta

RECEIVED: Eta

RECEIVED: Theta

当然,如果前面的Observable不为空,则switchIfEmpty()将无效并且不使用指定的Observable。

2.6、sorted()

如果您具有实现Comparable<T>的有限Observable<T>发射项,则可以使用sorted()对发射进行排序。 在内部,它将收集所有发射物,然后按其排序顺序重新发射它们。 在下面的代码片段中,我们对Observable<Integer>的发射进行排序,以使其以自然顺序发射:

import io.reactivex.Observable;

public class Ch3_17 {
    public static void main(String[] args) {
        Observable.just(6, 2, 5, 7, 1, 4, 9, 8, 3)
                .sorted()
                .subscribe(System.out::println);
    }
}

输出结果如下:1 2 3 4 5 6 7 8 9

当然,这可能会对性能产生一些影响,因为它将在再次发射之前收集所有发射内存。 如果将其用于无限的Observable,则可能会出现OutOfMemory错误。

您还可以提供Comparator作为参数来指定一个明确的排序标准。我们可以提供Comparator来反转排序顺序,如下所示:

import io.reactivex.Observable;

import java.util.Comparator;

public class Ch3_18 {
    public static void main(String[] args) {
        Observable.just(6, 2, 5, 7, 1, 4, 9, 8, 3)
                .sorted(Comparator.reverseOrder())
                .subscribe(System.out::println);
    }
}

输出结果如下:9 8 7 6 5 4 3 2 1

由于Comparator是单抽象方法的接口,因此您可以使用lambda快速实现它。 指定代表两个发射的两个参数,然后将它们映射到它们的比较操作。 例如,我们可以使用它来根据字符串的长度对字符串发射进行排序。 这也使我们可以对未实现Comparable的项目进行排序:

import io.reactivex.Observable;

public class Ch3_19 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .sorted((x, y) -> Integer.compare(x.length(), y.length()))
                .subscribe(System.out::println);
    }
}

输出结果如下:

Beta
Alpha
Gamma
Delta
Epsilon

2.7、delay()

我们可以使用delay()运算符推迟发射。 它将保留所有收到的发射,并将每个发射延迟指定的时间段。 如果我们想将发射延迟三秒钟,我们可以这样做:

import io.reactivex.Observable;

import java.util.concurrent.TimeUnit;

public class Ch3_20 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .delay(3, TimeUnit.SECONDS)
                .subscribe(s -> System.out.println("Received: " + s));
        sleep(5000);
    }

    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出结果如下:

Received: Alpha
Received: Beta
Received: Gamma
Received: Delta
Received: Epsilon

因为delay()在不同的调度程序(例如Observable.interval())上运行,所以我们需要利用sleep()方法使应用程序保持足够长的生命周期才能看到这种情况。 每次发射将延迟三秒钟。 您可以传递一个可选的第三个布尔值参数,该参数指示您是否还希望延迟错误通知。

对于更高级的情况,您可以将另一个Observable用作您的delay()参数,这将延迟发射,直到另一个Observable发出某些东西为止。

注意,有一个delaySubscription()运算符,它将延迟对它之前的Observable的订阅,而不是延迟每个个体的发射。

2.8、repeat()

repeat()运算符将在onComplete()指定的次数后向上游重复订阅。

例如,对于给定的Observable,我们可以通过将long 2用作repeat()的参数来重复两次发射,如以下代码片段所示:

import io.reactivex.Observable;

public class Ch3_21 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .repeat(2)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:

Received: Alpha
Received: Beta
Received: Gamma
Received: Delta
Received: Epsilon
Received: Alpha
Received: Beta
Received: Gamma
Received: Delta
Received: Epsilon

如果您未指定数字,它将无限重复,直到在每个onComplete()之后永远重新订阅。 还有一个repeatUntil()运算符,它接受布尔值lambda参数,并将继续重复直到产生true。

2.9、scan()

scan()方法是滚动聚合器。 对于每次发射,都将其添加到累积中。 然后,它将发出每个增量累加。例如,您可以通过将lambda传递给scan()方法来产生每个累加的滚动总和,该方法将每个下一个累加添加到累加器中:

import io.reactivex.Observable;

public class Ch3_22 {
    public static void main(String[] args) {
        Observable.just(5, 3, 7, 10, 2, 14)
                .scan((accumulator, next) -> accumulator + next)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:
Received: 5

Received: 8

Received: 15

Received: 25

Received: 27

Received: 41

它发出的初始值为5,这是它收到的第一个发射。 然后,它收到3,然后加到5,发出8。此后,收到7,再加到8,发出15,依此类推。 这不必仅用于汇总。 您可以创建多种累加(甚至是非数学累加,例如字符串串联或布尔归约)。

请注意,scan()与reduce()非常相似,我们将很快对其进行介绍。 注意不要混淆两者。 scan()方法为每个发射发射滚动累积,其中reduce()产生单个发射,反映了onComplete()调用后的最终累积。 scan()可以安全地用于无限的Observable,因为它不需要onComplete()调用。

您还可以为第一个参数提供初始值,并将其聚合为与发出的类型不同的类型。 如果我们要发出发射的滚动计数,我们可以提供一个初始值0,并为每个发射加上1。 请记住,将首先发出初始值,因此如果您不希望产生初始值,请在scan()之后使用skip(1):

import io.reactivex.Observable;

public class Ch3_23 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .scan(0, (total, next) -> total + 1)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:

Received: 0
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5

三、减少运算符

您可能会想在某个时刻进行一系列发射并将其合并为单一发射(通常通过“单一”发射)。 我们将介绍一些完成此操作的运算符。

请注意,几乎所有这些运算符都只能在调用onComplete()的有限Observable上工作,因为通常,我们只能合并有限数据集。 我们将在介绍这些运算符时探索这种行为。

3.1、count()

将发射量合并为一个最简单的运算符是count()。 一旦调用onComplete(),它将计算发射量并通过Single发射,如下所示:

import io.reactivex.Observable;

public class Ch3_24 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .count()
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

像大多数归约运算符一样,不应将其用于无限的Observable。 它会挂起并无限工作,永远不会发出计数或调用onComplete()。 您应该考虑使用scan()来发出滚动计数。

3.2、reduce()

reduce()运算符在语法上与scan()相同,但是仅在源调用onComplete()时才发出最终的累加。 根据您使用的重载,它可以产生Single或Maybe。 如果要发射所有整数发射的总和,则可以将每一个发射并添加到滚动总计中。 但是它只会在完成后发出:

import io.reactivex.Observable;

public class Ch3_25 {
    public static void main(String[] args) {
        Observable.just(5, 3, 7, 10, 2, 14)
                .reduce((total, next) -> total + next)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: 41

与scan()类似,您可以提供一个种子参数,该参数将作为累积的初始值。 如果要将发射转换为单个逗号分隔的字符串值,则可以这样使用reduce(),如下所示:

import io.reactivex.Observable;

public class Ch3_26 {
    public static void main(String[] args) {
        Observable.just(5, 3, 7, 10, 2, 14)
                .reduce("", (total, next) -> total + (total.equals("") ?
                        "" :
                        ",") + next)
                .subscribe(s -> System.out.println("Received: " +
                        s));
    }
}

输出结果如下:Received: 5,3,7,10,2,14

我们提供了一个空字符串作为种子值,并保持滚动串联总数并不断添加。 我们使用三元运算符检查总和是否为种子值,并返回空字符串(如果不是)而不是逗号,以防止前面的逗号。

您的种子值应该是不可变的,例如整数或字符串。 如果可变,则可能会产生不良影响,对于这些情况,您应该使用collect()或seedWith()),稍后我们将进行介绍。 例如,如果要将T发射减少到一个集合中,例如List<T>,请使用collect()而不是reduce()。 对每个订阅使用相同的列表,而不是每次都创建一个空的空列表,使用reduce()会有不希望的副作用。

3.3、all()

all()运算符将验证每个发射均符合指定条件,并返回Single<Boolean>。 如果它们全部通过,它将发出True。 如果遇到失败,它将立即发出False。 在以下代码段中,我们针对六个整数发出测试,以验证它们均小于10:

import io.reactivex.Observable;

public class Ch3_27 {
    public static void main(String[] args) {
        Observable.just(5, 3, 7, 11, 2, 14)
                .all(i -> i < 10)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: false

当all()运算符遇到11时,它立即发出False并调用onComplete()。 它甚至没有达到2或14,因为那是不必要的工作。 它已经找到一个无法通过整个测试的元素。

3.4、any()

any()方法将检查至少一个发射是否满足特定条件,并返回Single<Boolean>。 一旦找到符合条件的发射,它将发射true,然后调用onComplete()。 如果它处理所有发射并且发现所有发射都是错误的,它将发出false并调用onComplete()。在下面的代码片段中,我们发射四个日期字符串,将它们转换为LocalDate发射,并测试当月的任何发射 6月或更晚的时间:

import io.reactivex.Observable;

import java.time.LocalDate;

public class Ch3_28 {
    public static void main(String[] args) {
        Observable.just("2016-01-01", "2016-05-02", "2016-09-12",
                "2016-04-03")
                .map(LocalDate::parse)
                .any(dt -> dt.getMonthValue() >= 6)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: true

当它的日期为2016-09-12时,它立即发出true并调用onComplete()。 它没有继续处理2016-04-03。

3.5、contains()

contains()运算符将检查是否从Observable发出特定元素(基于hashCode()/ equals()实现)。 它会返回一个Single<Boolean>,如果找到则返回true,否则返回false。在下面的代码片段中,我们输出从1到10000的整数,并检查是否使用contains()发出了9563的数字 :

import io.reactivex.Observable;

public class Ch3_29 {
    public static void main(String[] args) {
        Observable.range(1, 10000)
                .contains(9563)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: true

您可能会猜到,一旦找到该元素,它将发出true并调用onComplete()并处置该操作。 如果源调用onComplete()而未找到该元素,则它将发出false。

四、集合运算符

集合运算符会将所有发射物累积到一个集合中,例如list或map,然后将整个集合作为一个发射物发射。 集合运算符是减少运算符的另一种形式,因为它们将发射量合并为一个。 我们将分别介绍它们,因为它们本身就是一个重要的类别。

请注意,为此您应避免减少发射量。 它会破坏响应式编程的好处,在响应式编程中,项目是从头到尾一次地处理。 仅当以某种方式对发射进行逻辑分组时,才希望将其合并到集合中。

4.1、toList()

常见的集合运算符是toList()。 对于给定的Observable<T>,它将收集传入的发射到ListT>中,然后将整个List<T>作为单个发射推送(通过Single <List<T>>)。 在以下代码片段中,我们将字符串发射收集到List<String>中。 在前面的Observable信号onComplete()之后,该列表被向前推送到要打印的观察者:

import io.reactivex.Observable;

public class Ch3_30 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .toList()
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: [Alpha, Beta, Gamma, Delta, Epsilon]

默认情况下,toList()将使用标准ArrayList实现。 您可以选择指定一个整数参数来用作容量提示,这将优化ArrayList的初始化以大致预期该数量的项目:

import io.reactivex.Observable;

public class Ch3_31 {
    public static void main(String[] args) {
        Observable.range(1, 1000)
                .toList(1000)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

如果要指定除ArrayList之外的其他列表实现,则可以提供Callable lambda作为构造一个的参数。 在以下代码段中,我提供了一个CopyOnWriteArrayList实例作为我的列表:

import io.reactivex.Observable;

import java.util.concurrent.CopyOnWriteArrayList;

public class Ch3_32 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .toList(CopyOnWriteArrayList::new)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

如果您想使用Google Guava的不可变列表,这会有些麻烦,因为它是不可变的,并且使用了构建器。 在本节后面,我们将向您展示如何使用collect()进行此操作。

4.2、toSortedList()

toList()的另一种形式是toSortedList()。 这会将发射物收集到清单中,该清单会根据其Comparator实施情况对这些项目进行自然排序。 然后,它将把排序后的List<T>向前发送给观察者:

import io.reactivex.Observable;

public class Ch3_33 {
    public static void main(String[] args) {
        Observable.just(6, 2, 5, 7, 1, 4, 9, 8, 3)
                .toSortedList()
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: [1, 2, 3, 4, 5, 6, 7, 8, 9]

像sorted()一样,您可以提供Comparator作为参数以应用其他排序逻辑。 您也可以像toList()一样为后备ArrayList指定初始容量。

4.3、toMap() and toMultiMap()

对于给定的Observable<T>,toMap()运算符将发射收集到Map<K,T>中,其中K是从lambda Function<T,K>自变量派生的密钥类型,该参数为每次发射生成密钥。

如果我们想将字符串收集到Map<Char,String>中,其中每个字符串都以其第一个字符为键,则可以这样做:

import io.reactivex.Observable;

public class Ch3_34 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .toMap(s -> s.charAt(0))
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: {A=Alpha, B=Beta, D=Delta, E=Epsilon, G=Gamma}

s-> s.charAt(0)lambda参数获取每个字符串,并派生与之配对的密钥。 在这种情况下,我们将字符串的第一个字符作为键。

如果我们想产生不同于发射的其他值以与键关联,我们可以提供第二个lambda参数,将每个发射映射到不同的值。 例如,我们可以将每个第一个字母键与该字符串的长度进行映射:

import io.reactivex.Observable;

public class Ch3_35 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .toMap(s -> s.charAt(0), String::length)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: {A=5, B=4, D=5, E=7, G=5}

默认情况下,toMap()将使用HashMap。 您还可以提供第三个lambda参数,该参数提供不同的map实现。 例如,我可以提供ConcurrentHashMap而不是HashMap:

import io.reactivex.Observable;

import java.util.concurrent.ConcurrentHashMap;

public class Ch3_36 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .toMap(s -> s.charAt(0), String::length,
                        ConcurrentHashMap::new)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

请注意,如果我有一个映射到多个发射的键,则该键的最后一个发射将替换后续的发射。 如果我将字符串长度作为每次发射的键,Alpha将被Gamma取代,Gamma将被Delta取代:

import io.reactivex.Observable;

public class Ch3_37 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .toMap(String::length)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: {4=Beta, 5=Delta, 7=Epsilon}

如果要给定键映射到多个发射,则可以改用toMultiMap(),它将为每个键维护一个对应值的列表。 然后,将Alpha,Gamma和Delta都放在一个列表中,该列表以长度五为键:

import io.reactivex.Observable;

public class Ch3_38 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .toMultimap(String::length)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: {4=[Beta], 5=[Alpha, Gamma, Delta], 7=[Epsilon]}

4.4、collect()

当没有任何集合运算符满足您的需要时,您始终可以使用collect()运算符指定其他类型来将项目收集到其中。

例如,没有toSet()运算符可以将发射物收集到Set<T>中,但是您可以快速使用collect()有效地做到这一点。 您将需要指定两个使用lambda表达式构建的参数:initialValueSupplier(将为新的Observer提供一个新的HashSet),以及collector(将每个发射添加到该HashSet的方式):

import io.reactivex.Observable;

import java.util.HashSet;

public class Ch3_39 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .collect(HashSet::new, HashSet::add)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: [Gamma, Delta, Alpha, Epsilon, Beta]

现在,我们的collect()运算符将发出单个HashSet<String>,其中包含所有
发射值。

将发射物放入可变对象时,请使用collect()而不是reduce(),并且每次都需要一个新的可变对象种子。 我们也可以对不是直接收集实现的棘手情况使用collect()。

假设您将Google Guava添加为依赖项(https://github.com/google/guava),并且想要将发射收集到ImmutableList中。 要创建ImmutableList,必须调用其builder()工厂以产生ImmutableList.Builder<T>。 然后,您调用其add()方法将项目放入构建器中,然后调用build(),该调用返回密封的,最终的ImmutableList ,无法修改。

要将发射收集到ImmutableList中,可以为第一个lambda参数提供ImmutableList.Builder ,然后在第二个参数中通过其add()方法添加每个元素。 一旦完全填充,它将发出ImmutableList.Builder<T>,并且您可以将其映射()到其build()调用以发出ImmutableList<T>:

import com.google.common.collect.ImmutableList;
import io.reactivex.Observable;

public class Ch3_40 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .collect(ImmutableList::builder,
                        ImmutableList.Builder::add)
                .map(ImmutableList.Builder::build)
                .subscribe(s -> System.out.println("Received: " + s));
    }
}

输出结果如下:Received: [Alpha, Beta, Gamma, Delta, Epsilon]

同样,collect()运算符有助于将发射收集为RxJava不能提供的任何任意类型。

五、错误恢复运算符

跨许多运算符的Observable链中可能会发生异常,这取决于您在做什么。 我们已经知道了onError()事件,该事件是通过Observable链传递给Observer的。 之后,订阅将终止,并且不再发生发射。 但是有时候,我们想在异常到达观察者之前拦截它们,并尝试某种形式的恢复。 我们不一定会假装该错误从未发生过并期望发射会恢复,但是我们可以尝试重新订阅或切换到可观察的替代来源。

我们仍然可以使用前一个方法,只是不能使用RxJava运算符,我们很快会看到。 如果您发现错误恢复运算符不能满足您的需求,那么您很有可能可以创造性地进行组合。

对于这些示例,让我们将每个整数发射除以10,其中发射之一为0。这将导致向观察者发出“ / by zero”异常,如以下代码片段所示:

import io.reactivex.Observable;

public class Ch3_41 {
    public static void main(String[] args) {
        Observable.just(5, 2, 4, 0, 3, 2, 8)
                .map(i -> 10 / i)
                .subscribe(i -> System.out.println("RECEIVED: " + i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

输出结果如下:

RECEIVED: 2
RECEIVED: 5
RECEIVED: 2
RECEIVED ERROR: java.lang.ArithmeticException: / by zero

5.1、onErrorReturn() 和 onErrorReturnItem()

当您希望在发生异常时使用默认值时,可以使用onErrorReturnItem()。 如果我们想在发生异常时发出-1,我们可以这样做:

import io.reactivex.Observable;

public class Ch3_42 {
    public static void main(String[] args) {
        Observable.just(5, 2, 4, 0, 3, 2, 8)
                .map(i -> 10 / i)
                .onErrorReturnItem(-1)
                .subscribe(i -> System.out.println("RECEIVED: " + i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

输出结果如下:

RECEIVED: 2
RECEIVED: 5
RECEIVED: 2
RECEIVED: -1

您还可以提供Function <Throwable,T>来使用lambda动态产生值。 这使您可以访问Throwable,您可以使用它来确定返回的值,如以下代码片段所示:

import io.reactivex.Observable;

public class Ch3_43 {
    public static void main(String[] args) {
        Observable.just(5, 2, 4, 0, 3, 2, 8)
                .map(i -> 10 / i)
                .onErrorReturn(e -> -1)
                .subscribe(i -> System.out.println("RECEIVED: " + i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

onErrorReturn()的位置很重要。 如果将其放在map()运算符之前,则不会捕获该错误,因为它发生在onErrorReturn()之后。 要拦截发出的错误,它必须在发生错误的位置的下游。

请注意,即使我们发出-1来处理错误,该序列在此之后仍然终止。 我们没有得到应该遵循的3、2或8。 如果要恢复发射,则只想在map()运算符中处理可能发生错误的错误。 您可以这样做代替onErrorReturn()或onErrorReturnItem():

import io.reactivex.Observable;

public class Ch3_44 {
    public static void main(String[] args) {
        Observable.just(5, 2, 4, 0, 3, 2, 8)
                .map(i -> {
                    try {
                        return 10 / i;
                    } catch (ArithmeticException e) {
                        return -1;
                    }
                })
                .subscribe(i -> System.out.println("RECEIVED: " +
                                i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

输出结果如下:

RECEIVED: 2
RECEIVED: 5
RECEIVED: 2
RECEIVED: -1
RECEIVED: 3
RECEIVED: 5
RECEIVED: 1

5.2、onErrorResumeNext()

与onErrorReturn()和onErrorReturnItem()类似,onErrorResumeNext()非常相似。 唯一的区别是,在发生异常的情况下,它接受另一个Observable作为参数来发出潜在的多个值,而不是单个值。

这是一些人为设计的内容,可能没有业务用例,但是如果发生错误,我们可以发出三种-1发射:

import io.reactivex.Observable;

public class Ch3_45 {
    public static void main(String[] args) {
        Observable.just(5, 2, 4, 0, 3, 2, 8)
                .map(i -> 10 / i)
                .onErrorResumeNext(Observable.just(-1).repeat(3))
                .subscribe(i -> System.out.println("RECEIVED: " + i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

输出结果如下:

RECEIVED: 2
RECEIVED: 5
RECEIVED: 2
RECEIVED: -1
RECEIVED: -1
RECEIVED: -1

我们还可以将其传递给Observable.empty()以在出现错误的情况下安静地停止发射,并优雅地调用onComplete()函数:

import io.reactivex.Observable;

public class Ch3_46 {
    public static void main(String[] args) {
        Observable.just(5, 2, 4, 0, 3, 2, 8)
                .map(i -> 10 / i)
                .onErrorResumeNext(Observable.empty())
                .subscribe(i -> System.out.println("RECEIVED: " + i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

输出结果如下:
RECEIVED: 2

RECEIVED: 5

RECEIVED: 2

与onErrorReturn()类似,您可以提供Function<TThrowable,Observable<T>> lambda来根据发出的Throwable动态生成Observable,如代码片段所示:

import io.reactivex.Observable;

public class Ch3_47 {
    public static void main(String[] args) {
        Observable.just(5, 2, 4, 0, 3, 2, 8)
                .map(i -> 10 / i)
                .onErrorResumeNext((Throwable e) ->
                        Observable.just(-1).repeat(3))
                .subscribe(i -> System.out.println("RECEIVED: " + i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

输出结果如下:

RECEIVED: 2

RECEIVED: 5

RECEIVED: 2

RECEIVED: -1

RECEIVED: -1

RECEIVED: -1

5.3、retry()

尝试恢复的另一种方法是使用retry()运算符,该运算符具有多个参数重载。 它将重新订阅前面的Observable,希望不再有此错误。

如果您不带任何参数调用retry(),它将为每个错误重新订阅无数次。 您需要小心retry(),因为它可能会造成混乱。 在示例中使用它会导致它无限重复地发出这些整数:

import io.reactivex.Observable;

public class Ch3_48 {
    public static void main(String[] args) {
        Observable.just(5, 2, 4, 0, 3, 2, 8)
                .map(i -> 10 / i)
                .retry()
                .subscribe(i -> System.out.println("RECEIVED: " + i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

输出结果如下:

RECEIVED: 5
RECEIVED: 2
RECEIVED: 2
RECEIVED: 5
RECEIVED: 2
RECEIVED: 2
RECEIVED: 5
RECEIVED: 2

指定一个固定的retry()次数,然后放弃该错误并将错误告知观察者,可能会更安全。 在以下代码段中,我们将仅重试两次:

import io.reactivex.Observable;

public class Ch3_49 {
    public static void main(String[] args) {
        Observable.just(5, 2, 4, 0, 3, 2, 8)
                .map(i -> 10 / i)
                .retry(2)
                .subscribe(i -> System.out.println("RECEIVED: " + i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

输出结果如下:

RECEIVED: 2
RECEIVED: 5
RECEIVED: 2
RECEIVED: 2
RECEIVED: 5
RECEIVED: 2
RECEIVED: 2
RECEIVED: 5
RECEIVED: 2
RECEIVED ERROR: java.lang.ArithmeticException: / by zero

您还可以提供Predicate<Throwable>或BiPredicate<Integer,Throwable>来有条件地控制何时尝试retry()。 当给定的Boolean Supplier lambda为false时,retryUntil()运算符将允许重试。 还有一个高级retryWhen()运算符,它支持诸如延迟重试之类的任务的高级合成。

六、动作运算符

在结束本章时,我们将介绍一些有用的运算符,这些运算符可以帮助调试以及获得对Observable链的可见性。 这些是action或doOn运算符。

6.1、doOnNext(), doOnComplete(), 和 doOnError()

这三个运算符:doOnNext(),doOnComplete()和doOnError()就像将一个微型Observer放在Observable链的中间。

使用doOnNext()运算符可以窥视从运算符发出并进入下一个的每个发射。 该运算符不会以任何方式影响操作或转换发射。 我们只是为链中该点发生的每个事件创建一个副作用。 例如,我们可以在将每个字符串映射到其长度之前对每个字符串执行操作。 在这种情况下,我们将通过提供Consumer<T> lambda来打印它们:

import io.reactivex.Observable;

public class Ch3_50 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .doOnNext(s -> System.out.println("Processing: " + s))
                .map(String::length)
                .subscribe(i -> System.out.println("Received: " + i));
    }
}

输出结果如下:

Processing: Alpha
Received: 5
Processing: Beta
Received: 4
Processing: Gamma
Received: 5
Processing: Delta
Received: 5
Processing: Epsilon
Received: 7

您还可以利用do After Next(),它在将发射传递到下游之前而不是之前执行操作。

使用onComplete()运算符可以在Observable链中的某个点调用onComplete()时触发操作。 这有助于查看Observable链的哪些点已完成,如以下代码片段所示:

import io.reactivex.Observable;

public class Ch3_51 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .doOnComplete(() -> System.out.println("Source is done emitting!"))
                .map(String::length)
                .subscribe(i -> System.out.println("Received: " + i));
    }
}

输出结果如下:

Received: 5
Received: 4
Received: 5
Received: 5
Received: 7
Source is done emitting!

而且,当然,onError()会窥视链上发出的错误,您可以对其执行操作。 这有助于将运算符放在两个运算符之间,以找出导致错误的原因:

import io.reactivex.Observable;

public class Ch3_52 {
    public static void main(String[] args) {
        Observable.just(5, 2, 4, 0, 3, 2, 8)
                .doOnError(e -> System.out.println("Source failed!"))
                .map(i -> 10 / i)
                .doOnError(e -> System.out.println("Division failed!"))
                .subscribe(i -> System.out.println("RECEIVED: " + i),
                        e -> System.out.println("RECEIVED ERROR: " + e)
                );
    }
}

输出结果如下:

RECEIVED: 2
RECEIVED: 5
RECEIVED: 2
Division failed!
RECEIVED ERROR: java.lang.ArithmeticException: / by zero

我们在两个地方使用了doOnError()来查看错误首次出现的位置。 由于我们没有看到Source failed! 打印,但我们看到"Division failed!",我们可以推断出错误发生在map()运算符中。

结合使用这三个运算符,可以深入了解您的Observable运算的作用或快速产生副作用。

您也可以使用doOnEach()为onNext(),onComplete()和onError()指定所有三个动作。 subscribe()方法接受这三个操作作为lambda参数或整个Observer<T>。 就像将subscribe()放在Observable链的中间一样!还有一个doOnTerminate()运算符,它会为onComplete()或onError()事件触发。

6.2、doOnSubscribe() 和 doOnDispose()

另外两个有用的操作运算符是doOnSubscribe()和doOnDispose()。doOnSubscribe()在订阅发生在Observable链中的那一刻触发特定的Consumer<Disposable>。 如果您要在该操作中调用dispose(),它提供对Disposable的访问。 当在Observable链中的那一点执行处置时,doOnDispose()运算符将执行特定的操作。

发生订阅和处置时,我们都使用两个运算符进行打印,如以下代码片段所示。 如您所料,我们首先看到了订阅事件。 然后,发射物通过,然后最终进行处置:

import io.reactivex.Observable;

public class Ch3_53 {
    public static void main(String[] args) {
        Observable.just("Alpha", "Beta", "Gamma", "Delta",
                "Epsilon")
                .doOnSubscribe(d -> System.out.println("Subscribing!"))
                .doOnDispose(() -> System.out.println("Disposing!"))
                .subscribe(i -> System.out.println("RECEIVED: " + i));
    }
}

输出结果如下:

Subscribing!
RECEIVED: Alpha
RECEIVED: Beta
RECEIVED: Gamma
RECEIVED: Delta
RECEIVED: Epsilon
Disposing!

请注意,doOnDispose()可以针对多余的处置请求多次触发,或者如果没有以某种形式或其他形式处置则根本不会触发。 另一种选择是使用doFinally()运算符,该运算符将在下游调用或废弃onComplete()或onError()之后触发。

6.3、doOnSuccess()

记住Maybe和Single类型没有onNext()事件,而是一个onSuccess()运算符来传递单个发射。 因此,如下面的代码片段所示,这两种类型都没有doOnNext()运算符,而是doOnSuccess()运算符。 实际上,它的用法应该像doOnNext():

import io.reactivex.Observable;

public class Ch3_54 {
    public static void main(String[] args) {
        Observable.just(5, 3, 7, 10, 2, 14)
                .reduce((total, next) -> total + next)
                .doOnSuccess(i -> System.out.println("Emitting: " + i))
                .subscribe(i -> System.out.println("Received: " + i));
    }
}

输出结果如下:

Emitting: 41
Received: 41

七、总结

在本章中,我们讨论了很多内容,希望到现在为止,您已经开始看到RxJava有很多实际用途。我们介绍了各种抑制和转换发射物并将其减少为某种形式的单一发射物的运营商。您了解了RxJava如何提供可靠的方法来从错误中恢复以及了解可观察的链对操作符的作用。

如果您想了解有关RxJava运算符的更多信息,可以在线找到许多资源。 Marblediagram是Rx文档的一种流行形式,可以直观地显示每个运算符的工作方式。 rxmarbles.com(http://rxmarbles.com)网站是一个流行的交互式Web应用程序,可让您拖动大理石发射并查看每个运算符的受影响行为。还有一个RxMarbles Android应用程序(https://play.google.com/store/apps/details?id=com.moonfleet.rxmarbles)

您可以在Android设备上使用的功能。当然,您还可以在ReactiveX网站(http://reactivex.io/documentation/operators.html)上看到运算符的完整列表。

信不信由你,我们才刚刚开始。 本章仅介绍基本运算符。 在接下来的章节中,我们将介绍执行强大行为(例如并发和多播)的运算符。 但是在此之前,让我们继续结合可观察对象的运算符。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值