RxJava学习 - 7. Basic Operators

Suppressing operators

有些operators能抑制不符合要求的emissions。他们的原理很简单,对于被取消资格的emission,不调用下游的onNext()函数,
也就不会发给下游的Observer了。我们已经看过filter(),它可能是最通用的抑制operator。我们从它开始。

filter()

对于给定的Observable,filter()接受Predicate。你提供给它一个lambda,把每个emission映射成一个Boolean值,如果值是false,就不发给下游。
例如,你能使用filter(),只允许长度超过5的字符串emissions:

import io.reactivex.Observable;

public class Launcher {
    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));
    }
}

注意:如果所有的emissions都失败了,Observable返回的是空。

take()

take()有两个重载。一个发射一定数量以后就调用onComplete()。它处置整个订阅,不会再发射:

import io.reactivex.Observable;

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

注意,如果你接收的emissions少于你指定的数量,它简单地emit它所获得的,然后调用onComplete()。
另一个重载是在指定的时间内接收,然后调用onComplete()。当然,cold Observable会很快emit,不是好例子。
可以使用Observable.interval(),下面的例子每300毫秒发射,但是只接收2秒钟:

import io.reactivex.Observable;
import java.util.concurrent.TimeUnit;

public class Launcher {
    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(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }   
}

上面的例子,2秒内,你只能得到6个emissions。
注意,还有一个takeLast(),在调用onComplete()之前,将只接受最后的一定数量的emissions(或者是time duration)。它内部有一个队列。

skip()

skip()和take()相反。它忽略指定数量的emissions,然后一个一个地emit。如果想跳过一个Observable的前90个emissions,代码可能是这样的:

import io.reactivex.Observable;

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

像take()那样,它也有接受time duration的重载。也有skipLast(),在调用onComplete()之前,它会跳过最后的一定数量(或者time duration)的items。

takeWhile() and skipWhile()

take()的另一个变种是takeWhile(),每个emission都满足条件时才接受。下面的例子,当emissions小于5就一直接受。碰到条件不满足的那一刻,调用onComplete():

import io.reactivex.Observable;

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

就像takeWhile(),还有一个skipWhile()。当emissions符合条件时,它就保持跳过。一旦条件不再符合,开始接受emissions。
在下面的代码里,当emissions小于等于95时,我们跳过它们:

import io.reactivex.Observable;

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

takeUntil()类似takeWhile(),但是它接受另一个Observable当参数。它将保持接受,直到参数Observable发布(push)一个emission。
skipUntil()有类似的行为,它也接受另一个Observable当参数。它将保持跳过,直到参数Observable发布(push)一个emission。

distinct()

distinct()将emit每个唯一的emission,抑制重复的。使用发射的对象的hashCode()/equals()判断是否相等。如果想发射不同长度的字符串序列,可以这么写:

import io.reactivex.Observable;

public class Launcher {
    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内,跟踪以前捕获的唯一值。也可以使用lambda参数,映射每个emission到一个key,来判断是否相等。下面的例子,使用字符串的长度判断是否唯一,发射的是字符串而不是他们的长度:

import io.reactivex.Observable;

public class Launcher {
    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

distinctUntilChanged()

distinctUntilChanged()忽略重复的连续的emissions。它可以忽略掉重复,直到他们改变了。如果相同的值被重复发射,所有的重复将被忽略,直到发射了新值。下一个重复值也会被忽略,等等:

import io.reactivex.Observable;

public class Launcher {
    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

就像distinct(),你可以提供一个可选的参数,通过lambda映射,作为一个key。看下面的代码:

import io.reactivex.Observable;

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

elementAt()

可以使用index指定某个emission,index是一个从0开始的Long。当该item被发射,调用onComplete()。
比如下面的代码,获取来自一个Observable的第四个emission:

import io.reactivex.Observable;

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

你可能没注意到,elementAt()返回一个Maybe。
还有另一个elementAtOrError(),将返回Single,如果没找到元素,就发射一个错误。singleElement()把一个Observable转换成Maybe,但是没有元素的时候,会产生错误。最后,firstElement()和lastElement()将分别产生可能的第一个或者最后一个emission。

Transforming operators

map()

对于一个给定的Observable,map()使用提供的Function<T,R> lambda把一个T emission转换成一个R emission。
我们已经使用了很多次,把字符串转成长度。下面是一个新例子,我们把原生的date字符串使用map()转换成LocalDate emission:

import io.reactivex.Observable;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Launcher {
    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));        
    }
}

map()一个一个地转换每个emission。如果需要一对多转换,需要使用flatMap()或者concatMap(),以后再讲。

cast()

一个简单的,类似map的操作,cast每个emission到一个不同的type,是cast()。如果我们想接受Observable,把每个cast到一个对象,返回
Observable,可以使用map():

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

但是我们可以使用cast(),我们可以简单地传我们想要cast to的class,像这样:

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

如果你发现由于继承和多态,类型已经很混乱,想cast成通用的base类型是很困难的。

startWith()

对于给定的Observable,startWith()允许你在其他emissions前面,插入一个T emission。例如,如果我们有一个Observable,
发射的item是想打印的菜单,我们能使用startWith()增加一个title:

import io.reactivex.Observable;

public class Launcher {
    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);
    }       
}

如果想在开头插入多个,使用startWithArray(),它接受可变参数:

import io.reactivex.Observable;

public class Launcher {
    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);
    }       
}

defaultIfEmpty()

如果一个给定的Observable是空的,但是我们想采用single emission,可以使用defaultIfEmpty(),这样可以指定一个默认值。
如果我们有一个Observable,只接受以“Z”开始的值,如果为空,就采用“None”, 可以这样写:

import io.reactivex.Observable;

public class Launcher {
    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);
    }       
}

switchIfEmpty()

如果源Observable为空,switchIfEmpty()就采用指定一个不同的Observable发射的值:

import io.reactivex.Observable;

public class Launcher {
    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

sorted()

如果你有一个有限的Observable,发射的item实现了Comparable,就可以使用sorted()排序。在内部,它会手机所有的emissions,经过排序再发射。看下面的代码:

import io.reactivex.Observable;

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

你也可以提供一个Comparator,作为参数,指定排序规则:

import io.reactivex.Observable;

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

由于Comparator是一个single-abstract-method的接口,可以使用lambda。两个参数代表两个emissions,使用他们做比较操作:

import io.reactivex.Observable;

public class Launcher {
    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

delay()

delay()保存任何接收到的emissions,每个都延迟指定的时间周期再发射。下面的代码延迟3秒:

import io.reactivex.Observable;
import java.util.concurrent.TimeUnit;

public class Launcher {
    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(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }   
}

delay()使用不同的调度器(例如Observable.interval()),我们需要使用sleep()方法,让程序活到可以看到输出。你可以传递一个可选的Boolean参数,指示你是否想要延迟错误通知。
你也可以传递另一个Observable做你的delay()参数,它将延迟发射,直到那个Observable发射了一些东西。
注意,delaySubscription()会延迟订阅之前的Observable,而不是延迟每个emission。

repeat()

repeat()在onComplete()之后,重复订阅上游一定的次数。
例如,对于给定的Observable,给repeat()传参数2,就会重复两次:

import io.reactivex.Observable;

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

输出是

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

如果不指定数量,会无限重复,在每个onComplete()之后,永远重复订阅。
repeatUntil()接受一个Boolean Supplier lambda参数,在它产生true之前,将一直重复。

scan()

scan()是滚动聚合的。对每个emission,你把它加到一个累积。然后,它将发射每个增量累积。
例如,给scan()传一个lambda,能发射一个每个emission的滚动的和:

import io.reactivex.Observable;

public class Launcher {
    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,这是它接收到的第一个emission。然后,它收到3,加到5,发射8。然后,收到7,加到8,发射15。等等。
它不只是能用来累加。你能增加各种累积(甚至非数学的,比如字符串级联或者Boolean reductions)。
注意,scan()很像reduce()。但是,scan()为每个emission发射是滚动值,而reduce()在onComplete()之后产生一个emission反映最终的累积。scan()能安全地用于无限的Observables,因为它v不需要调用onComplete()。
也可以为第一个参数提供一个初始值,也可以聚合成不同类型的值。如果想发射滚动的数量,可以提供一个初始值0,然后每个emission加1。记住,初始值会被发射,所以在scan()之后,使用skip(1),就不发射初始值了:

import io.reactivex.Observable;

public class Launcher {
    public static void main(String[] args) {
        Observable.just(5, 3, 7, 10, 2, 14)
                .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
Received: 6

Reducing operators

有时候,你可能会想接受一系列的emissions,合成一个emission(通常使用Single发射)。我们现在看一下相关的一些operators。注意,他们基本上都作用于有限的Observable。

count()

这是最简单的一个,统计emissions的数量,一旦调用onComplete(),就作为一个Single发射出去。例如:

import io.reactivex.Observable;

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

它不能被用于无限的Observable,否则会挂起来,不会发射总数,也不会调用onComplete()。

reduce()

reduce()和scan()的语法相同,但是,它只发射最后的累积(在调用onComplete()的时候)。根据不同的重载,产生Single或者Maybe:

import io.reactivex.Observable;

public class Launcher {
    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));
    }       
}

和scan()类似,你能提供参数seed,作为累积的初值。如果我们想把emissions转换成一个逗号分隔的字符串,可以这样写:

import io.reactivex.Observable;

public class Launcher {
    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

其中的seed是空字符串。
注意,seed应该是不可变的,例如整数或者字符串。如果是可变的,可能会产生不良副作用,这时候,你应该用collect()(或者seedWith())。例如,如果你想把T emissions变成一个集合(比如List),使用了collect()而不是reduce()。使用reduce()的副作用是每个订阅使用同一个集合,而不是每次都是新建的空的集合。

all()

all()验证每个emission,根据是否满足条件返回Single。只有全部按组条件,才发射True。只要碰到失败的,就立刻发射False:

import io.reactivex.Observable;

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

如果在一个空Observable上调用all(),将根据vacuous truth发射True。

any()

只要有一个emission符合条件,any()就发射True。发现第一个符合条件的,马上调用onComplete():

import io.reactivex.Observable;
import java.time.LocalDate;

public class Launcher {
    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));        
    }
}

如果在一个空Observable上调用any(),将根据vacuous truth发射False。

contains()

contains()检测一个特定的元素(基于hashCode()/equals())。如果找到,发射True:

import io.reactivex.Observable;

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

Collection operators

Collection operators把所有的emissions放到一个集合,比如list或者map,然后把整个集合当作一个emission。
注意,尽量避免把emissions放到一个集合。你应该知道,响应式编程的好处就是从开始到最后,一次一个地处理。只有在逻辑分组的时候,才应该使用这些操作。

toList()

对于一个给定的Observable,toList()会把所有的emissions收集进一个List,然后整个发射出去(使用Single<List>)。
下面的代码,在前面的Observable调用onComplete()之后,发射list:

import io.reactivex.Observable;

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

toList()默认使用ArrayList。你可以使用一个整数参数作为capacityHint,设置期望的容量,优化ArrayList的初始化行为:

import io.reactivex.Observable;

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

如果你不想用ArrayList,可以提供一个Callable lambda构造一个:

import io.reactivex.Observable;
import java.util.concurrent.CopyOnWriteArrayList;

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

toSortedList()

toSortedList()的元素使用Comparator实现做排序:

import io.reactivex.Observable;

public class Launcher {
    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));
    }
}

像sorted(),可以提供一个Comparator参数,实现不同的排序逻辑。也可以像toList()那样指定初始容量。

toMap() and toMultiMap()

对于一个给定的Observable,toMap()把emissions收集进Map<K, T>,使用Function<T, K>参数生成每个emission的key。
如果我们想把字符串收集进Map<Char, String>,key是每个字符串的首字母,可以这样实现:

import io.reactivex.Observable;

public class Launcher {
    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}

如果想产生不同的value,可以提供第二个lambda参数,来映射每个emission。比如,我们能映射每个字符串的长度:

import io.reactivex.Observable;

public class Launcher {
    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));
    }       
}

toMap()默认使用HashMap。你可以提供第三个lambda参数,提供一个不同的map实现。比如ConcurrentHashMap:

import io.reactivex.Observable;
import java.util.concurrent.ConcurrentHashMap;

public class Launcher {
    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));
    }       
}

注意,如果一个key映射到多个emissions,后来的emission会替换之前的值。比如,我们使用字符串的长度当作每个emission的key,
Alpha被Gamma代替,而Gamma被Delta代替:

import io.reactivex.Observable;

public class Launcher {
    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}

如果想用一个key映射多个emissions,可以使用toMultiMap(),它会为每个key维护一个list:

import io.reactivex.Observable;

public class Launcher {
    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]}

collect()

可以使用collect(),把元素收集进一个其他类型,比如Set。你需要提供两个参数,他们都是lambda表达式:initialValueSupplier提供一个新的HashSet或者一个新的Observer,collector指定每个emission怎么加进这个HashSet:

import io.reactivex.Observable;
import java.util.HashSet;

public class Launcher {
    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));
    }       
}

当你需要把emissions放进一个可变对象的时候,就使用collect()代替reduce(),你每次都需要一个新的可变对象的种子。
如果你使用Google Guava,想把emissions收集进一个ImmutableList。(要增加一个ImmutableList,你不得不调用它的builder()工厂生成一个build(),它返回不能被修改的final ImmutableList)。
你的第一个lambda参数可以是一个ImmutableList.Builder,然后每个元素都使用第二个参数的add()方法。再使用map(),调用它的build()产生ImmutableList:

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

public class Launcher {
    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));        
    }
}

Error recovery operators

Observable链上可能发生Exceptions。onError()事件会向下传导到Observer,然后订阅结束了,不会再有emissions。
但是,有时候,我们想在exceptions到达Observer之前拦截他们,尝试恢复。我们不能假装错误没发生,期望继续emissions,但是,我们能尝试重订阅或者切换到一个备用的源Observable。
我们可以像以前那样,不用RxJava operators。
如果你发现错误恢复operators不符合你的要求,可以创造性地组合他们。
比如下面的例子,会给Observer发射异常:

import io.reactivex.Observable;

public class Launcher {
    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

onErrorReturn() and onErrorReturnItem()

当抛异常的时候,如果你想使用默认值,你可以使用onErrorReturnItem()。比如下面的代码,抛异常就发射-1:

import io.reactivex.Observable;

public class Launcher {
    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>动态产生值。这样做,你可以访问Throwable,来决定返回值:

import io.reactivex.Observable;

public class Launcher {
    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()的前面,就不会捕获错误。要拦截已发射的错误,必须在错误发生的下游
注意,发生错误时,我们发射了-1,但是,序列还是被终止了。如果想继续,可以这样写:

import io.reactivex.Observable;

public class Launcher {
    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

onErrorResumeNext()

onErrorResumeNext()和onErrorReturn()、onErrorReturnItem()类似。只是它接受另一个Observable当参数,在发生错误时,发射可能的多个值,而不是一个值:

import io.reactivex.Observable;

public class Launcher {
    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()悄悄地停止emissions,优雅地调用onComplete():

import io.reactivex.Observable;

public class Launcher {
    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)
                );
    }       
}

也可以提供Function<Throwable, Observable>,动态产生一个Observable:

import io.reactivex.Observable;

public class Launcher {
    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)
                );
    }       
}

retry()

另一个办法是retry()。它重新订阅先前的Observable,希望不再发生错误。
如果你的retry()没参数,对每个错误都重订阅无限次

import io.reactivex.Observable;

public class Launcher {
    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)
                );
    }       
}

指定次数比较安全:

import io.reactivex.Observable;

public class Launcher {
    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或者BiPredicate<Integer, Throwable>做条件控制。retryUntil()允许给定的BooleanSupplier是false时重试。
还有一个retryWhen(),它支持诸如延迟重试这样的高级的任务组合。

Action operators

下面的operators有助于调试。

doOnNext(), doOnComplete(), and doOnError()

doOnNext()、doOnComplete()和doOnError()就像把一个小的Observer放进Observable链的之中。
doOnNext()允许你偷看每个emission,它不会做任何修改:

import io.reactivex.Observable;

public class Launcher {
    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

doAfterNext(),在emission传给下游之后执行。
onComplete()允许你在调用onComplete()的时候,执行某动作。有助于你知道Observable链何时完成:

import io.reactivex.Observable;

public class Launcher {
    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));
    }       
}

onError()在发生错误时调用。有助于你知道错误应该归咎于哪个元素:

import io.reactivex.Observable;

public class Launcher {
    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)
                );
    }       
}

可以使用doOnEach()代替onNext()、onComplete()和onError()。使用doOnTerminate()代替onComplete()和onError()。

doOnSubscribe() and doOnDispose()

doOnSubscribe()在订阅发生的时候,发射一个Consumer,可以用来调用dispose()。当disposal被执行的时候,doOnDispose()会执行特定的动作。
下面的代码,我们在订阅和disposal发生的时候,打印些:

import io.reactivex.Observable;

public class Launcher {
    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()被调用,或者被下游处置的时候发射。

doOnSuccess()

Maybe和Single没有onNext()事件,但是在发射之后会onSuccess():

import io.reactivex.Observable;

public class Launcher {
    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));
    }       
}

猜你喜欢

转载自blog.csdn.net/weixin_43364172/article/details/84102951