Chapter 2: Observables and Subscribers-观察者与可观察对象
本章重点
- Observable的使用
- Observer的使用
- 其他创建Observable的工厂方法
- Single、Completable和Maybe
- Disposable
Observable是如何工作的
在我们做其他事情之前,我们需要学习的是一个Observable序列是如何将item通过链推送给Observer的。在最高层,Observable传递这三类事件:
-
onNext()
:每次从源推送一个item到Observer -
onComplete()
:将一个完成事件推送给Observer,表明后续不再有onNext()
调用 -
onError()
:将一个错误事件推送给Observer,通常由观察者来定义如何处理它。除非使用retry
操作符来拦截这一错误,否则可观察链将终止,并不在发出任何事件
这三个事件是Observer中的抽象方法,稍后我们将介绍其中的一些实现。我们先观察它们在日常场景中是如何使用的。
提示🤠:在RxJava1.0中
onComplete()
其实叫做onCompleted()
使用Observable.create()
现在,让我们使用Observable.create()来创建一个Observable。相对而言source是我们观测链的起点。
Observable.create()
允许我们通过lambda表达式创建一个Observable emitter。我们能够调用Observable emitter的onNext()
方法来发射(一次)数据(emissions),以及调用一次onComplete()
来通知发射完成,之后便不再有事件发出。这些onNext()
调用将把item连接到观察者,它将打印每一个项目,如下代码所示:
package chapter2;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
public class Launcher {
public static void main(String[] args) {
Observable<String> source = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
}
});
source.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String s) {
System.out.println("RECEIVED:" + s);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
System.out.println("onComplete");
}
});
// lambada
Observable<String> sourceByLambda = Observable.create(emitter -> {
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
});
sourceByLambda.subscribe(s -> System.out.println("REVEIVED:" + s));
}
}
它们将输出(非lambda版本):
RECEIVED:Alpha
RECEIVED:Beta
RECEIVED:Gamma
RECEIVED:Delta
RECEIVED:Epsilon
onComplete
lamda版本:
RECEIVED:Alpha
RECEIVED:Beta
RECEIVED:Gamma
RECEIVED:Delta
RECEIVED:Epsilon
提示🤠:在RxJava1.0中,需要使用Observable.fromEmitter()来替换Observable.create(),因为后者在RxJava1.0中的作用与RxJava2.0,并且只针对RxJava高级用户。
onNext()是处理每一项数据的一种方式:从“Alpha”开始,到调用链中的下一步。在这个例子中,下一步是Observer,它使用s -> System.out.println("RECEIVED: " + s)
这一lambda表达式来打印每一项数据。这一Lambda表达式是在Observer的onNext()
中调用的,我们稍后会更仔细的观察Observer。
警告⚠️:需要注意的是,Observable规定(http://reactivex.io/documentation/contract.html)数据必须按照顺序一次性发送完毕,而不能通过Observable并行的发送。这似乎是一个限制,但实际上这简化了程序,使得Rx更易使用。我们将在第六章:并发性与并行性中学习一些强大的技巧,从而在不违背Observable规定的情况下有效的利用并发性和并行性。
onComplete()
用于通知Observer,不会再有数据推过来了。Observable可以是无限的,如果是这种情况,则永远不会调用onComplete()
事件。从技术来讲,Observable可以通过不再调用的onNext()
方式来停止发出数据,从而不使用onComplete()
方法。但是如果Observable不再计划发送数据,这可能是一个糟糕的设计。
尽管这个在这个例子中不会抛出异常,但是我们仍旧可以在Observable.create()代码块中捕获可能会发生的异常,并通过onError()发射异常。这样,异常就会被推送到调用链上交由Observer处理。前面的例子中我们的Observer不处理异常,但是你可以像下面这么做:
package chapter2;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
public class Launcher {
public static void main(String[] args) {
Observable<String> source = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
try{
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
}catch (Exception e){
emitter.onError(e);
}
}
});
// Observer bind Observable
source.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String s) {
System.out.println("RECEIVED:" + s);
}
@Override
public void onError(Throwable e) {
System.out.println(e);
}
@Override
public void onComplete() {
System.out.println("onComplete");
}
});
// lambda版本
Observable<String> sourceByLambda = Observable.create(emitter -> {
try{
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
}catch (Exception e){
emitter.onError(e);
}
});
sourceByLambda.subscribe(s->System.out.println("RECEIVED:" + s),
Throwable::printStackTrace);
}
}
注意,onNext()
、onComplete()
和onError
并不一定会直接发送给最终的Observer。它们还可以发送给调用链中的下一个操作符。在下面的代码中,我们将使用map()
和filter()
操作符派生新的Observable,这些操作符将在源Observable后到最终Observer打印数据间进行操作。
package chapter2;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
public class Launcher {
public static void main(String[] args) {
Observable<String> source = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
try {
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
});
Observable<Integer> lengths = source.map(new Function<String, Integer>() {
@Override
public Integer apply(String s) throws Exception {
return s.length();
}
});
Observable<Integer> filtered = lengths.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) throws Exception {
return integer >= 5;
}
});
filtered.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
System.out.println("RECEIVED:" + integer);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
throwable.printStackTrace();
}
});
// lambda版本
Observable<String> sourceByLambda = Observable.create(emitter -> {
try {
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
Observable<Integer> lengthsByLambda = sourceByLambda.map(String::length);
Observable<Integer> filteredByLambda = lengthsByLambda.filter(integer -> integer >= 5);
filteredByLambda.subscribe(s -> System.out.println("RECEIVED:" + s),
Throwable::printStackTrace);
}
}
运行后将输出:
RECEIVED:5
RECEIVED:5
RECEIVED:5
RECEIVED:7
使用map()
和filter()
操作符可在source Observer与Observer之间,onNext()
将会把每一项数据交由map()
操作符处理。在内部,它将充当中介观察者,将每一个字符串转换为其长度。反过来,这将调用onNext()
将该整数传递给filter()
,而lambda表达式i -> i >= 5
将过滤掉长度小于5的数据。最后filter()
操作符调用onNext()
将每一项数据推送给最终的Observer,将结果打印出来。
值得注意的是,map()
操作符将从原来的Observable<String>
上产生一个新的可观察对象Observable<Integer>
。filter()
操作符同样会返回一个Observable<Integer>
,但是忽略了那些没有达到输出条件的数据。
由于像map()
和filter()
这样的操作符产生了新的可观察对象(在内部通过使用Observer来接收数据实现),我们可以将所有的可观察对象与下一个操作符连接在一起,不必再将每一步都保存在一个中间变量中。
package chapter2;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
public class Launcher {
public static void main(String[] args) {
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
try {
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
}).map(new Function<String, Integer>() {
@Override
public Integer apply(String s) throws Exception {
return s.length();
}
}).filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) throws Exception {
return integer >= 5;
}
}).subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
System.out.println("RECEIVED:" + integer);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
throwable.printStackTrace();
}
});
// lambda版本
Observable<String> sourceByLambda = Observable.create(emitter -> {
try {
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
sourceByLambda.map(String::length)
.filter(integer -> integer >= 5)
.subscribe(s -> System.out.println("RECEIVED:" + s),
Throwable::printStackTrace);
}
}
同样会输出如下结果:
RECEIVED:5
RECEIVED:5
RECEIVED:5
RECEIVED:7
这种链式调用的方式在响应式编程中是常见的(并且是推荐的方式)。它的可读性很好,从左至右,从上到下像书的结构一样,这对可维护性和易读性有很大的帮助。
警告⚠️:在RxJava2.0中,可观察对象不再支持发射一个空值。如果你创建了一个试图发射空值得可观察对象,将得到一个非空y异常。如果你需要发射一个空值,请考虑使用Java 8或是Google的Guava库的Optional将其封装起来。
例如:
package chapter2;
import io.reactivex.Observable;
import java.util.Optional;
public class Launcher {
public static void main(String[] args) {
Optional<Void> voidOptional = Optional.empty();// 等同于Optional.ofNullable(null)
Observable.create(emitter -> {
emitter.onNext(voidOptional);
emitter.onComplete();
}).subscribe(s -> System.out.println(s));
}
}
这将会输出:
Optional.empty
有关Optional的更多信息看这里http://www.importnew.com/6675.html
使用Observable.just()
在我们继续查看subscribe()
方法之前,先要提醒的是,你可能并不需要经常使用Observable.create()
来创建可观察对象。读完本章后面的内容你就会发现,它更多的是帮助我们使用本来不是响应式的元数据。所以一般我们仅仅需要使用其他精简版的工厂方法来创建Observable
。
在之前使用Observable.create()
的例子中,我们可以使用Observable.just()
方法来实现同样的功能。它最多可以发射10个同类型的数据,然后为每一项数据调用onNext()
方法发射它们,并在发射完成后调用onComplete()
方法:
package chapter2;
import io.reactivex.Observable;
public class Launcher {
public static void main(String[] args) {
Observable.just("Alpha", "Beta", "Gamma", "Delta", "Espilon")
.map(String::length)
.filter(i -> i >= 5)
.subscribe(s -> System.out.println("RECEIVED:" + s),
Throwable::printStackTrace,() ->System.out.println("onComplete"));
}
}
我们也可以使用Observable.formIterable()
来发射任何Iterbale
类型的数据,比如说List
。它也会通过调用onNext()
方法来发射每一项数据,然后在迭代完成后调用onComplete()
方法。你可能会频繁的使用这一工厂方法,因为在java中迭代器是很常见的,并且很容易写出如下代码:
package chapter2;
import io.reactivex.Observable;
import java.util.Arrays;
import java.util.List;
public class Launcher {
public static void main(String[] args) {
List<String> items=Arrays.asList("Alpha", "Beta", "Gamma", "Delta", "Espilon");
Observable.fromIterable(items)
.map(String::length)
.filter(i -> i>=5)
.subscribe(s -> System.out.println("RECEIVED:" + s),
Throwable::printStackTrace,() ->System.out.println("onComplete"));
}
}
我们将在本章后续引入更多创建Observable的工厂方法,但是现在让我们把重点放在了解Observer上。
Observer接口
实际上onNext()
,onComplete()
以及onError()
方法都定义在Observer
类中,RxJava通过这一抽象接口来传递这些事件。下面这段代码就是Observer
接口的定义。暂时不要关注onSubscribe()
方法,因为我们将在本章后面的内容中介绍它。请注意其他三个方法:
package io.reactivex;
import io.reactivex.disposables.Disposable;
public interface Observer<T> {
void onSubscribe(Disposable d);
void onNext(T value);
void onError(Throwable e);
void onComplete();
}
Observer
和Observable
有时可能是相对的。在同一上下文中,调用链的起点和数据发出的地方的Observable
都可以称为source Observable
。在前面的示例中,你可以认为Observable.create()
和Observable.just()
返回的Observable
是source Observable
。但是在filter()
操作符中,它的Observable
是从map()
操作符返回的。它无从得知真正的起点在哪,它只知道它正在接收来自它上游map()
发射的数据。
相反,由操作符返回的每一个Observable
内部都是Observer,它接收、转换来自上游的数据,并作为中继将数据传递给下游。它不知道下游究竟是一个操作符还是调用链尾部的最后一个Observer
。当我们经常提到的Observer
指的是处于调用连尾部的最终消耗了数据的观察者。但是对于map()
和filter()
这些操作符来说,其内部同样利用了Observer
。
我们将会在第九章:Transformers 和 自定义操作符中学习更多关于自定义操作符的知识,现在我们将着重于如何使用Observer
的subscribe()
方法。
警告⚠️:在RxJava 1.0中,
Subseriber
本质上是RxJava 2.0中的Observer
。在RxJava 1.0中,Subseriber
和RxJava 2.0的Observer
类,同样定义了三个事件,但是Subseriber
是subseribe()
方法的参数,并且它实现了Observer
接口。在RxJava 2.0中,仅当我们提到Flowables时才存在Subseriber
。我们将在第八章:Flowables 和 背压中说到它。
实现并订阅一个Observer
当你调用Observable
的subscribe()
方法时,一个Observer
就会通过实现它的抽象方法来使用这三个事件。我们可以手动实现一个Observer
,并将它实例化后传递给subscribe()
方法,而不是像之前那样将lambda表达式作为参数。现在不要理会onSubscribe()
方法,我们先提供一个空实现,在本章后面的内容中,我们会谈到过它。
package chapter2;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
public class Launcher {
public static void main(String[] args) {
Observable<String> source = Observable.just("Alpha", "Beta", "Gamma", "Delta", "Espilon");
Observer<Integer> myObserver = new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
// 不去使用disposable,现在不要理会它
}
@Override
public void onNext(Integer value) {
System.out.println("RECEIVED:" + value);
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
System.out.println("Done!");
}
};
source.map(String::length)
.filter(i -> i>=5)
.subscribe(myObserver);
}
}
这将会输出:
RECEIVED:5
RECEIVED:5
RECEIVED:5
RECEIVED:7
Done!
我们快速创建了一个Observer<Integer>
类型的Observer
,它接收整数类型的参数作为长度数据。我们的Observer
作为调用链的尾部接收数据。这样一来,意味着我们可以将这些数据写入数据库、文本文件,作为服务器响应,显示在UI上,或者(像本例一样)只是打印在控制台中。
让我们从开始发射字符串的源头开始进一步观察这个例子。我们先是定义了我们的Observer
,并将其作为参数,传递给了调用链尾部的subscribe()
方法。注意,每个字符串都被转换成了它的长度。onNext()
方法接收每个长度数据,并通过System.out.println("RECEIVED:" + value)
将其打印到控制台。这个过程中不会抛出任何异常,但如果在我们的调用链中的任意一个阶段抛出了异常,它将被推送到我们实现的onError()
方法中,然后打印堆栈信息。最后,当发射源没有数据时(发射完 "Espilon"之后),这会导致调用链上的观察者的onComplete()
被逐一调用,直到最后一个onComplete()
方法将Done
打印到控制台中。
使用lambda表达式快速实现Observer
实现一个Observer
代码有点冗长且过程麻烦。幸运的是,subscribe()
的重载方法接收我们用lambda表达式实现这三个事件。我们大多数情况下可能更喜欢这样——指定三个用逗号分隔开的lambda表达式作为参数:onNext()
lambda、onError()
lambda 和 onComplete()
lambda。对于前面的例子来说,我们可以使用这三个lambda表达式来实现那三个方法:
Consumer<Integer> onNext = i -> System.out.println("RECEIVED: " + i);
Action onComplete = () -> System.out.println("Done!");
Consumer<Throwable> onError = Throwable::printStackTrace;
我们可以将这三个lambda表达式作为subscribe()
的参数,subscribe()
会将使用它们为我们实现一个Observer
。这样要简洁的多,需要的样本代码也少得多:
package chapter2;
import io.reactivex.Observable;
public class Launcher {
public static void main(String[] args) {
Observable<String> source =
Observable.just("Alpha", "Beta", "Gamma", "Delta",
"Epsilon");
source.map(String::length)
.filter(i -> i >= 5)
.subscribe(i -> System.out.println("RECEIVED: " + i),
Throwable::printStackTrace, () -> System.out.println("Done!"));
}
}
输出如下:
RECEIVED: 5
RECEIVED: 5
RECEIVED: 5
RECEIVED: 7
Done!
请注意,subscribe()
还有其他的重载。你可以忽略onComplete()
,只实现onNext()
和onError()
。当你不需要时onComplete()
,像下面这段代码这样,将不再为onComplete()
执行任何操作:
package chapter2;
import io.reactivex.Observable;
public class Launcher {
public static void main(String[] args) {
Observable<String> source =
Observable.just("Alpha", "Beta", "Gamma", "Delta",
"Epsilon");
source.map(String::length)
.filter(i -> i >= 5)
.subscribe(i -> System.out.println("RECEIVED: " + i),
Throwable::printStackTrace);
}
}
输出如下:
RECEIVED: 5
RECEIVED: 5
RECEIVED: 5
RECEIVED: 7
正如你前面看到的那样,你甚至可以忽略onError()
,仅指定onNext()
:
package chapter2;
import io.reactivex.Observable;
public class Launcher {
public static void main(String[] args) {
Observable<String> source =
Observable.just("Alpha", "Beta", "Gamma", "Delta",
"Epsilon");
source.map(String::length)
.filter(i -> i >= 5)
.subscribe(i -> System.out.println("RECEIVED: " + i));
}
}
然而,在生产环境中还是要尽量实现onError()
。在调用链中的任何地方发生的异常都会让Observable
终止发射数据,并将异常交由onError()
处理。如果你不指定onError()
,那么异常将无法得到处理:
提示🤠:当发生错误时,你可以使用
retry()
操作符重新订阅一个Observable
来尝试恢复。我们将在下一章节介绍如何实现这一操作。
值得注意的是,subscribe()
的大多数重载方法(包括我们刚刚使用的lambda缩写)都返回了Disposable
对象,我们并没有对它进行任何操作。disposable可以使我们在Observer
中与Observable
断开连接,这样发射就会被提前终止。这对于一直运行或者是长时间运行的Observable
可是至关重要的,我们将在本章的末尾介绍disposable。
Observables的冷于热
Observable
和Observer
间究竟是什么样的微妙关系,与Observable
的实现有关。Observables
的冷与热是它的一个重要特性,这定义了当存在多个Observable
时会发生的情况,首先,我们先介绍Observables的冷。
Observables的冷
冷的Observable就像是一张CD。它能够被每一个听者重新播放,所以每一个人在任何时候都能够听到完整的音乐。同样,Observable也会为每一个Observer
重发数据,确保每一个Observer都能够接收到全部数据。大多数数据驱动的Observable都是冷的,包括Observable.just()
和Observable.fromIterable()
等工厂方法。
在下面的例子中,有两个惯着差订阅了同一个Observable
。这个Observable
将会把所有数据先发送给第一个Observer
然后调用onComplete()
。然后,它将会给第二个Observer
发送所有的数据然后调用onComplete()
。这两个观察者从不同的数据流接收到了相同的数据,这便是冷Observable的一个典型应用:
package chapter2;
import io.reactivex.Observable;
public class Launcher {
public static void main(String[] args) {
Observable<String> source =
Observable.just("Alpha", "Beta", "Gamma", "Delta",
"Epsilon");
source.subscribe(i -> System.out.println("observer1 RECEIVED: " + i));
source.subscribe(i -> System.out.println("observer2 RECEIVED: " + i));
}
}
输出如下:
observer1 RECEIVED: Alpha
observer1 RECEIVED: Beta
observer1 RECEIVED: Gamma
observer1 RECEIVED: Delta
observer1 RECEIVED: Epsilon
observer2 RECEIVED: Alpha
observer2 RECEIVED: Beta
observer2 RECEIVED: Gamma
observer2 RECEIVED: Delta
observer2 RECEIVED: Epsilon
即使第二个Observer
通过操作符转换了它的数据集,仍旧能够从数据集中得到自己的数据流。使用像map()
和filter()
操作冷的Obserbvable
得到的仍旧是冷的Observable:
package chapter2;
import io.reactivex.Observable;
public class Launcher {
public static void main(String[] args) {
Observable<String> source =
Observable.just("Alpha", "Beta", "Gamma", "Delta",
"Epsilon");
source.subscribe(i -> System.out.println("observer1 RECEIVED: " + i));
source.map(String::length)
.filter(i -> i >= 5)
.subscribe(i -> System.out.println("observer2 RECEIVED: " + i));
}
}
输出如下:
observer1 RECEIVED: Alpha
observer1 RECEIVED: Beta
observer1 RECEIVED: Gamma
observer1 RECEIVED: Delta
observer1 RECEIVED: Epsilon
observer2 RECEIVED: 5
observer2 RECEIVED: 5
observer2 RECEIVED: 5
observer2 RECEIVED: 7
如前所述,发出有限数据集的Observable
通常都是冷的。
这是一个更加真实的案例:Dave Moten的RxJava-JDBC库允许你创建一个冷的Observable来查询数据库。我们并不会深入这个库。假设你想查询一个SQLite数据库,并且项目中引入了SQLite JDBC
驱动和RxJava-JDBC
库。你可以像下面这段代码所示一样,查询表中数据:
import com.github.davidmoten.rx.jdbc.ConnectionProviderFromUrl;
import com.github.davidmoten.rx.jdbc.Database;
import rx.Observable; import java.sql.Connection;
public class Launcher {
public static void main(String[] args) {
Connection conn = new ConnectionProviderFromUrl("jdbc:sqlite:/home/thomas/rexon_metals.db").get();
Database db = Database.from(conn);
Observable<String> customerNames =
db.select("SELECT NAME FROM CUSTOMER")
.getAs(String.class);
customerNames.subscribe(s -> System.out.println(s));
}
}
输出如下:
LITE Industrial
Rex Tooling Inc
Re-Barre Construction
Prairie Construction
Marsh Lane Metal Works
这个SQL驱动的Observable
是冷的。许多从数据库、文本文件或者是JSON等有限的数据源发出的Observable
都是冷的。重点是Observable
是如何运行的。RxJava-JDBC将会为每一个观察者都巡行一次该查询。这意味着,如果在第二个观察者订阅前数据发生了变化,那么它将得到与第一个观察者所得到的不同的数据。即使结果数据从底层的表中发生了改变,但是Observable
仍然是冷的,因为它仅仅是重新运行了这段查询。
再次强调,冷的Observbales将会以某种形式为每一个Observer
重新发送这个Observbale
所取得的数据。下面,我们将会介绍比数据更像事件的热的Observbales。
Observbales的热
你刚刚学习了什么是冷的Observable
,它的工作原理就像一张CD。而一个热的Observbale
则更像是一个电台。它像广播一样同时向所有的观察者发射数据。如果一个Observable
订阅了一个热的Observable
并接收了一些数据。这时另一个Observer
也订阅了它,那么他将错过之前发射的那些数据。就像电台一样,如果你切换的晚了些,那么你将错过那首歌。
从逻辑上来讲,热的Observble往往代表的是事件,而不是有限的数据集。这些事件能够携带数据,但是它们对时间敏感——后订阅的观察者会错过之前发射的数据。
举个栗子,一个JavaFX或者Android 的UI事件都能够被看做是热的Observable
。在JavaFX中,你可以使用Observable.create()
创建一个Observable<Bollean>
来包装ToggleButton
的selectedProperty()
。然后将布尔值转换为"UP"或者"DOWN"表示ToggleButton
处于开启状态还是关闭状态,之后使用一个Observer
在Label
中显示它,如下面的代码片段所示:
提示🤠:请在gradle中添加RxJavaFx的依赖:
compile 'io.reactivex.rxjava2:rxjavafx:2.2.2'
package chapter2;
import io.reactivex.Observable;
import io.reactivex.functions.Function;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MyJavaFxApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
System.out.println("start!");
ToggleButton toggleButton = new ToggleButton("TOGGLE ME");
Label label = new Label();
Observable<Boolean> selectedStates = valuesOf(toggleButton.selectedProperty());
selectedStates.map(selected -> selected?"DOWN":"UP")
.subscribe(label::setText);// lambda还能够引用实例的方法
VBox vBox = new VBox(toggleButton,label);
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
}
private static <T> Observable<T> valuesOf(final ObservableValue<T> fxObservable){
return Observable.create(observableEmitter -> {
// 发射初始状态
observableEmitter.onNext(fxObservable.getValue());
// 当状态改变时在监听器中发射当前状态
final ChangeListener<T> listener =
(observableValue,prev,current) -> observableEmitter.onNext(current);
fxObservable.addListener(listener);
});
}
}
输出如下:
[图片上传失败...(image-c7b37a-1517302658055)]
这是一个通过一个热的Observbale<Boolean>
来包装ToggleButton
的选择状态的JavaFX app。
注意⚠️:如果你使用的时OpenJDK,则需要单独导入JavaFX库。JavaFx库的可用版本可在甲骨文官方文档中查看http://www.oracle.com/technetwork/java/javase/downloads/index.html
JavaFX的ObservableValue
与RxJava的Observbale
没有任何关系。它是JavaFX所独有的,但是我们可以通过使用valuesOf()
工厂方法在ChangeListener
中调用onNext()
方法从而很容易的转换成了RxJava的Observable
。当你每次点击ToggleButton
时,这个Observable<Boolean>
都会发射一个true或者false来映射选择状态。这是一个简单的例子,表明这个Observbale
不但是在发射事件,而且也是在发射true或false这个数据。它将布尔值转换为字符串,并通过Observer
改变Label
中的文本。
在这个JavaFX的示例中,我们只有一个Observer
。如果我们让更多的观察者参与ToggleButton
数据发出之后的事件,那么这些新的观察者将错过这些数据。
JavaFX和Android上的UI事件是热的Observable的主要例子,但是你同样可以使用热的Observable来响应服务器请求。如果你为某个特定的话题发射消息的Twitter流创建一个Observbale
,那也将是一个热的Observbale。虽然许多热的Observable的源的数据都是无限的(infinite),但是它们也可以不这样。他们只需同时向所有观察者发射数据,并且不对那些迟到的观察者重发之前的数据即可。
提示🤠:RxJavaFX(以及RxAndroid)都有各类工厂方法帮你将UI事件与
Observbale
进行绑定。你可以使用RxJavaFX的valuesOf()
这一工厂方法简化上面的示例。
请注意,在这个JavaFX示例中我们同样没有处理disposal,我们将会在本章末尾再去讨论它。
ConnnectableObservbale
ConnectableObservable
是一个有用的热Observbale
。它能够接收任何Observable
,即使它是一个冷的,也能够将其转为热的然后将所有数据同时向所有的观察者同时发送一次。要进行这种转换,只需调用任意一个Observbale
的publish()
方法,就会产生一个ConnectableObservable
。
但是订阅后并不会自动发射数据。你需要调用connect()
方法来开始发射,这允许你提前设置好所有的观察者。请看下面这段代码:
package chapter2;
import io.reactivex.Observable;
import io.reactivex.observables.ConnectableObservable;
public class HotLauncher {
public static void main(String[] args){
ConnectableObservable<String> source = Observable
.just("Alpha","Beta","Delta","Gamma","Epsilon")
.publish();
source.subscribe(s -> System.out.println("observer1 RECEIVED: " + s));
source.map(String::length)
.subscribe(i -> System.out.println("observer2 RECEIVED: " + i));
//发射!
source.connect();
}
}
输出如下:
observer1 RECEIVED: Alpha
observer2 RECEIVED: 5
observer1 RECEIVED: Beta
observer2 RECEIVED: 4
observer1 RECEIVED: Delta
observer2 RECEIVED: 5
observer1 RECEIVED: Gamma
observer2 RECEIVED: 5
observer1 RECEIVED: Epsilon
observer2 RECEIVED: 7
请注意,第一个观察者接收到的是字符串,而另一个观察者接收到的是长度,它们以交错的形式打印数据。这两种订阅都是预先设置好的,然后通过调用connect()
来发射数据。两个观察者会同时收到这些数据:第一个观察者接收Alpha同时第二个观察者接收到5之后是Beta和4,后面的也一样,而不是让第一个观察者在第二个观察者之前处理完所有数据。第使用ConnectableObservable
强制将每一个数据都同时发送到所有观察者的情况,我们将在第五章多播中详细介绍。
ConnectableObservable
有助于防止为每个观察者重新发送数据的情况。当重新发送的代价很大,这种情况下,你也许宁愿将数据同时发送给所有观察者。即使在下游有多个观察者,你也可以通过ConnectableObservable
简单地强制上游的操作符使用单个流实例。多个观察者通常会在上游生成多个数据流实例,但是使用public()
返回的ConnectableObservable
将上游所有的数据流合并到同一个流中。同样,这些细微差别将在第五章多播中一一介绍。
现在只需要记住ConnectableObservable
是热的,因此,在connect()
之后订阅的观察者将错过之前发射的数据。