RxJava学习 - 3. Cold versus hot Observables

RxJava学习 - 3. Cold versus hot Observables

在Observable和Observer的关系中,有一个微妙的行为,依赖于Observable是如何实现的。Observables的一个主要特征是cold和hot,当有多个Observers时,这决定了Observables的行为。
首先,我们看看cold Observables。

Cold Observables

Cold Observables很像一个音乐CD,可以被每个听众重新播放,每个人可以在任何时刻听这些乐曲。
同样的,cold Observables可以为每个Observer,replay它的emissions,确保所有的Observers拿到全部数据。大多数数据驱动的Observables是cold,包括Observable.just()和Observable.fromIterable()工厂。
下面的例子,我们有两个Observers订阅了一个Observable。Observable首先发射所有的emissions给第一个Observer,然后调用onComplete()。然后,它再次发射所有的emissions给第二个Observer,然后调用onComplete()。通过两个分开的流,他们都接收到相同的数据。这是cold Observable的典型行为:

import io.reactivex.Observable;
public class Launcher {
    public static void main(String[] args) {
        Observable<String> source =
                Observable.just("Alpha", "Beta", "Gamma", "Delta", "Epsilon");
        //first observer
        source.subscribe(s -> System.out.println("Observer 1 Received: " + s));
        //second observer
        source.subscribe(s -> System.out.println("Observer 2 Received: " + s));        
    }
}

甚至在第二个Observer转换了emissions,它仍然获得一个自己的emissions流。使用map()和filter()仍然能产生cold的Observables:

import io.reactivex.Observable;
public class Launcher {
    public static void main(String[] args) {
        Observable<String> source =
                Observable.just("Alpha", "Beta", "Gamma", "Delta", "Epsilon");
        //first observer
        source.subscribe(s -> System.out.println("Observer 1 Received: " + s));
        //second observer
        source.map(String::length).filter(i -> i >= 5)
                .subscribe(s -> System.out.println("Observer 2 Received: " + s));     
    }
}

它的输出是

Observer 1 Received: Alpha
Observer 1 Received: Beta
Observer 1 Received: Gamma
Observer 1 Received: Delta
Observer 1 Received: Epsilon
Observer 2 Received: 5
Observer 2 Received: 5
Observer 2 Received: 5
Observer 2 Received: 7

这里有更实际的例子:Dave Moten’s RxJava-JDBC
允许你增加cold Observables实现SQL查询。如果你想查询一个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));
    }
}

SQL-driven Observable是cold。很多Observables从有限数据源(比如数据库、文本文件或者JSON)发射数据,他们都是cold。RxJava-JDBC为每个Observer执行查询。这意味着如果数据在两个订阅发生了变化,第二个Observer可以拿到与第一个不同的emissions。cold Observables会为每个Observer,重新生成emissions。

Hot Observables

hot Observable更像是一个广播电台。在同一个时刻,它向所有的Observers广播相同的emissions。如果一个Observer订阅了一个hot Observable,接收相同的emissions,然后来了另一个Observer,第二个Observer会错过这些emissions。就像广播电台,如果你打开晚了,你就听不到那首歌。
hot Observables通常代表事件而不是有限数据集。事件可以携带数据,但是,他们是时间敏感的组件,后来的observers会错过先前的数据。
例如,一个JavaFX或者一个Android UI事件能表示成一个hot Observable。在JavaFX里,你能使用一个ToggleButton对象的selectedProperty()方法
增加一个Observable。然后把布尔emissions转换成字符串,表示该按钮的状态(UP或者DOWN),使用一个Observer在Label里显示:

import io.reactivex.Observable;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class MyJavaFxApp extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        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);
        VBox vBox = new VBox(toggleButton, label);
        stage.setScene(new Scene(vBox));
        stage.show();
    }
    
    private static <T> Observable<T> valuesOf(final ObservableValue<T> fxObservable) {
        return Observable.create(observableEmitter -> {
            //emit initial state
            observableEmitter.onNext(fxObservable.getValue());
            //emit value changes uses a listener
            final ChangeListener<T> listener = (observableValue, prev, current) -> observableEmitter.onNext(current);
            fxObservable.addListener(listener);
        });
    }
}

JavaFX ObservableValue与RxJava Observable无关。它是JavaFX的,但是,使用valuesOf()工厂方法,很容易转成Observable。
每次点击ToggleButton,Observable会根据状态发射相应的true或者false。
JavaFX和Android的UI事件主要的hot Observables,你也能使用hot Observables反映服务器请求。如果你增加一个Observable
为某Twitter主题发射推文,这也是一个hot Observable。hot Observable不一定非要是无限的,只要是向所有的Observers共享emissions,
不replay错过的emissions的都是hot。

ConnectableObservable

一种有用的hot Observable是ConnectableObservable。它可以是任何Observable(包括cold),让它变hot,这样所有的emissions都只给Observers一次。想做这种转变,你只需要简单地在任何Observable上调用publish(),就会产生一个ConnectableObservable。但是,subscribing不能开始emissions。你需要调用它的connect()方法启动emissions的发射。这样,你可以预先设置你的Observers:

import io.reactivex.Observable;
import io.reactivex.observables.ConnectableObservable;
public class Launcher {
    public static void main(String[] args) {
        ConnectableObservable<String> source =
                Observable.just("Alpha", "Beta", "Gamma", "Delta", "Epsilon").publish();
        //Set up observer 1
        source.subscribe(s -> System.out.println("Observer 1: " + s));
        //Set up observer 2
        source.map(String::length)
                .subscribe(i -> System.out.println("Observer 2: " + i));
        //Fire!
        source.connect();        
    }
}

它的输出是

Observer 1: Alpha
Observer 2: 5
Observer 1: Beta
Observer 2: 4
Observer 1: Gamma
Observer 2: 5
Observer 1: Delta
Observer 2: 5
Observer 1: Epsilon
Observer 2: 7

注意,一个Observer收到字符串,另一个就收到长度,这两个是交错进行的。订阅都是预先设置好的,然后调用connect()开始发射。每个emission同时送给每个Observer。使用ConnectableObservable,强制每个emission同时发给所有的Observers,这叫做多播(multicasting),以后详细讲。
ConnectableObservable是有用的,防止数据被replay给每个Observer。
如果你觉得重播太昂贵了,你可能想这样做,每个emission只发射一次。甚至有多个下游Observers的时候,你也可以简单地强制上游使用一个流实例。
多个Observers通常会导致上游有多个流实例,但是,使用publish()返回ConnectableObservable来合并所有的上游形成一个流。
请记住,ConnectableObservable是hot。

猜你喜欢

转载自blog.csdn.net/weixin_43364172/article/details/84027845
今日推荐