第二章 Observables and Subscribers(觀察者和訂閱者)
導讀
在第1章“響應式思考”中,我們已經對Observable及其工作原理有所瞭解。 您可能對它的運作方式以及所擁有的實際應用有很多疑問。 本章將爲理解可觀察對象的工作方式以及與觀察者之間的關鍵關係提供基礎。 我們還將介紹幾種創建Observable的方法,以及通過介紹幾個運算符使它有用的方法。 爲了使本書的其餘部分順利進行,我們還將直接涵蓋所有關鍵的細微之處,以建立堅實的基礎,以後再也不會讓您感到驚訝。
這是本章要介紹的內容:
1、可觀察的(Observable)
2、觀察者(Observer)
3、其他可觀察工廠
4、單一,可完成和可能
5、一次性的
一、Observable
正如在第1章中所介紹的,Observable是基於推送的可組合迭代器。 對於給定的Observable ,它通過一系列運算符將typeT的項(稱爲發射)推送到最終到達最終的Observer,後者(Observer)是消耗項。 我們將介紹幾種創建Observable的方法,但首先,讓我們深入瞭解Observable如何通過其onNext(),onCompleted()和onError()調用工作。
1.1、Observable的工作原理
在執行其他任何操作之前,我們需要研究一下Observable如何將鏈上的項目順次傳遞到Observer。 在最高級別,一個Observable通過傳遞三種類型的事件來工作:
onNext():這一次將每個項目從源Observable一直傳遞到Observer。
onComplete():這將完成事件一直傳遞到Observer,指示不再發生onNext()調用。
onError():這會將錯誤傳遞給Observer,觀察者通常在其中定義如何處理它。 除非使用retry()運算符來攔截錯誤,否則Observable鏈通常會終止,並且不再發生發射。
這三個事件是Observer類型中的抽象方法,稍後我們將介紹一些實現。 目前,我們將務實地關注它們在日常使用中的工作方式。
在RxJava 1.0中,onComplete()事件實際上稱爲onCompleted()。
1.2、使用Observable.create()
讓我們從使用Observable.create()創建Observable開始。 相對而言,Observable源是發射在我們的可觀察鏈的起點的可觀察物。
Observable.create()工廠允許我們通過提供接收Observable發射器的lambda來創建Observable。 我們可以調用Observable發射器的onNext()方法將發射(一次一次)傳遞到鏈上,也可以調用onComplete()來完成信號並傳達將不再有其他項。 這些onNext()調用會將這些項目向上傳遞給Observer,它將在其中打印每個項目,如以下代碼片段所示:
import io.reactivex.Observable;
public class Ch2_1 {
public static void main(String[] args) {
Observable<String> source = Observable.create(emitter -> {
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
});
source.subscribe(s -> System.out.println("RECEIVED: " + s));
}
}
輸出如下:
RECEIVED: Alpha
RECEIVED: Beta
RECEIVED: Gamma
RECEIVED: Delta
RECEIVED: Epsilon
在RxJava 1.0中,確保使用Observable.fromEmitter()而不是Observable.create()。 後者與RxJava 1.0完全不同,僅適用於高級RxJava用戶。
onNext()方法是一種將每個項目(從Alpha開始)傳遞到鏈中下一步的一種方法。 在此示例中,下一步是觀察者,該觀察者使用s -> System.out.println("RECEIVED: " + s) lambda打印項目。 該lambda在Observer的onNext()調用中調用,稍後我們將更仔細地觀察Observer。
onComplete()方法用於將鏈條向上傳遞給觀察者,告知不再有物品要來。 Observable確實可以是無限的,如果是這種情況,將永遠不會調用onComplete()事件。 從技術上講,源可以停止發出onNext()調用,而從不調用onComplete()。 但是,如果源不再計劃發射,則這可能是錯誤的設計。
雖然這個特定的例子不太可能引發錯誤,但是我們可以捕獲可能在Observable.create()塊中發生的錯誤並通過onError()發出它們,這樣就可以將錯誤推上鍊並由Observer處理。 我們設置的特定觀察者不會處理異常,但是您可以這樣做,如下所示:
import io.reactivex.Observable;
public class Ch2_2 {
public static void main(String[] args) {
Observable<String> source = Observable.create(emitter -> {
try {
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
} catch (Throwable e) {
emitter.onError(e);
}
});
source.subscribe(s -> System.out.println("RECEIVED: " + s),
Throwable::printStackTrace);
}
}
請注意,onNext(),onComplete()和onError()不一定直接推送到最終的Observer。 他們還可以推動operator(函數)充當鏈中的下一步。在以下代碼中,我們使用map()和filter()運算符派生新的Observable,它們將在源Observable和最終Observer打印項目之間起作用:
import io.reactivex.Observable;
public class Ch2_3 {
public static void main(String[] args) {
Observable<String> source = Observable.create(emitter -> {
try {
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
} catch (Throwable e) {
emitter.onError(e);
}
});
Observable<Integer> lengths = source.map(String::length);
Observable<Integer> filtered = lengths.filter(i -> i >= 5);
filtered.subscribe(s -> System.out.println("RECEIVED: " +
s));
}
}
這是運行代碼後的輸出:
RECEIVED: 5
RECEIVED: 5
RECEIVED: 5
RECEIVED: 7
通過在源Observable和Observer之間使用map()和filter()運算符,onNext()將把每個項目交給map()運算符。在內部,它將充當中間觀察者,並將每個字符串轉換爲它的length()。反過來,這將調用filter()上的onNext()以傳遞該整數,並且lambda條件i-> i> = 5將抑制長度至少爲五個字符的發射。最後,filter()運算符將調用onNext()將每一項移交給最終的觀察者,在此處將其打印出來。
重要的是要注意,map()運算符將產生一個派生自原始Observable<String>的新Observable<Integer>。 filter()也將返回Observable<Integer>但忽略不符合標準的發射,由於map()和filter()等運算符產生新的Observables(內部使用Observer實現接收發射),因此我們可以將所有鏈使用下一個運算符返回Observable,而不是將每個變量不必要地保存到中間變量中:
import io.reactivex.Observable;
public class Ch2_4 {
public static void main(String[] args) {
Observable<String> source = Observable.create(emitter -> {
try {
emitter.onNext("Alpha");
emitter.onNext("Beta");
emitter.onNext("Gamma");
emitter.onNext("Delta");
emitter.onNext("Epsilon");
emitter.onComplete();
} catch (Throwable e) {
emitter.onError(e);
}
});
source.map(String::length)
.filter(i -> i >= 5)
.subscribe(s -> System.out.println("RECEIVED: " + s));
}
}
這是運行代碼後的輸出:
RECEIVED: 5
RECEIVED: 5
RECEIVED: 5
RECEIVED: 7
在響應式式編程中,以這種方式鏈接操作符是常見的(並鼓勵使用)。 它具有很好的可讀性,從左到右,從上到下都像一本書,這有助於維護性和可讀性。
在RxJava 2.0中,Observables不再支持發射空值。 如果創建試圖發出null值的Observable,則將立即獲得非null異常。 如果需要發出null,請考慮將其包裝在Java 8或Google Guava Optional中。
1.3、使用Observable.just()
在我們進一步討論subscribe()方法之前,請注意,您可能不需要經常使用Observable.create()。 它有助於連接某些非響應式的資源,本章稍後將在幾個地方看到這一點。 但通常情況下,我們使用簡化的工廠爲常見資源創建Observable。
在上一個帶有Observable.create()的示例中,我們可以使用Observable.just()完成此操作。 我們最多可以傳遞10個要發送的商品。 它將爲每個調用onNext()調用,然後在所有這些都被推送時調用onComplete():
import io.reactivex.Observable;
public class Ch2_5 {
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(s -> System.out.println("RECEIVED: " + s));
}
}
我們還可以使用Observable.fromIterable()從任何Iterable類型(例如List)發出項目。 它還將爲每個元素調用onNext(),然後在迭代完成後調用onComplete()。 您可能會經常使用此工廠,因爲Java中的Iterables很常見,並且可以很容易地使其具有響應性:
import io.reactivex.Observable;
import java.util.Arrays;
import java.util.List;
public class Ch2_6 {
public static void main(String[] args) {
List<String> items =
Arrays.asList("Alpha", "Beta", "Gamma", "Delta", "Epsilon");
Observable<String> source = Observable.fromIterable(items);
source.map(String::length).filter(i -> i >= 5)
.subscribe(s -> System.out.println("RECEIVED: " + s));
}
}
在本章的後面,我們將探索其他工廠來創建Observable,但是現在讓我們擱置它,瞭解有關Observers的更多信息。
二、Observer接口
onNext(),onComplete()和onError()方法實際上定義了Observer類型,它是在RxJava中實現的用於傳達這些事件的抽象接口。 這是代碼片段中顯示的RxJava中的Observer定義。 現在不要爲onSubscribe()煩惱,因爲我們將在本章結尾處介紹它。 只需引起您對其他三種方法的注意:
public interface Observer<T> {
void onSubscribe(@NonNull Disposable var1);
void onNext(@NonNull T var1);
void onError(@NonNull Throwable var1);
void onComplete();
}
觀察者和觀察來源是相對的。在一種情況下,“可觀察的”源是您的“可觀察的鏈”開始的地方和發射的來源。在前面的示例中,您可以說從Observable.create()方法或Observable.just()返回的Observable是源Observable。但是對於filter()運算符,從map()運算符返回的Observable是源。它不知道發射物來自何處,只是知道它正在接收來自其上游的操作員的發射物,這些發射物來自map()。
相反,方法函數返回的每個Observable內部都是一個Observer,它接收,轉換和發射到下一個下游Observer。它不知道下一個觀察者是鏈末尾的另一個函數還是最終的觀察者。當我們談論觀察者時,我們經常談論的是消耗發射的可觀察鏈末端的最終觀察者。但是每個運算符,例如map()和filter(),也在內部實現Observer。
我們將在第9章``變形和自定義運算符’'中詳細瞭解運算符的構建方式。目前,我們將重點放在對subscribe()方法使用Observer上。
在RxJava 1.0中,訂閱者實質上成爲RxJava 2.0中的觀察者。 RxJava 1.0中有一個Observer類型,它定義了三個事件方法,但是Subscriber是您傳遞給subscribe()方法的對象,並且實現了Observer。 在RxJava 2.0中,僅在談論Flowable時存在訂戶,我們將在第8章Flowables和Backpressure中進行討論。
2.1、實現和訂閱觀察者
當您在Observable上調用subscription()方法時,Observer通過實現其方法來使用這三個事件。 可以像之前那樣指定lambdaarguments,而不是實現Observer並將其實例傳遞給subscription()方法。 暫時不要爲onSubscribe()煩惱。 只需將其實現留空,直到我們在本章末尾討論它時:
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
public class Ch2_7 {
public static void main(String[] args) {
Observable<String> source =
Observable.just("Alpha", "Beta", "Gamma", "Delta",
"Epsilon");
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,它將接收整數長度的發射。我們的觀察員在可觀察鏈的末尾接收發射,並充當消耗發射的終點。通過消耗,這意味着它們到達了過程的末尾,在那裏它們被寫入數據庫,文本文件,服務器響應,在UI中顯示,或者(在這種情況下)僅打印到控制檯。
爲了進一步詳細說明此示例,我們從源頭的字符串發射開始。我們預先聲明瞭Observer並將其傳遞給Observable鏈末尾的subscribe()方法。請注意,每個字符串都將轉換爲其長度。 onNext()方法接收每個整數長度的發射,並使用System.out.println(“ RECEIVED:” value)進行打印。我們不會在運行此簡單過程時遇到任何錯誤,但是如果在Observable鏈中的任何位置確實發生了錯誤,它將被推送到Observer的onError()實現中,該處將打印Throwable的堆棧跟蹤。最後,當源沒有更多的發射時(按“ Epsilon”後),它將一直沿鏈向上調用onComplete()到Observer,在此將調用其onComplete()方法並打印 Done! 到控制檯。
2.2、lambda的速記觀察者
實現觀察者有點冗長和繁瑣。 幸運的是,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()方法,它將使用它們來爲我們實現一個Observer。 這更加簡潔,所需模板代碼更少:
import io.reactivex.Observable;
public class Ch2_8 {
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!"));
}
}
請注意,subscribe()還有其他重載。 您可以省略onComplete(),而僅實現onNext()和onError()。 這將不再對onComplete()執行任何操作,但是在某些情況下,您可能不需要以下操作:
import io.reactivex.Observable;
public class Ch2_9 {
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);
}
}
正如您在前面的示例中看到的那樣,您甚至可以省略onError,而只需指定onNext即可:
import io.reactivex.Observable;
public class Ch2_10 {
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()進行處理,然後終止Observable且不再發出任何錯誤。 如果您沒有爲onError指定操作,則將解決該錯誤。
您可以使用retry()運算符嘗試恢復並在發生錯誤時重新預訂Observable。 我們將在下一章介紹如何做到這一點。
重要的是要注意,大多數subscribe()重載變量(包括我們剛剛覆蓋的速記lambda變量)都返回一個我們沒有做任何事情的Disposable。 Disposable使我們能夠將觀察物與觀察者斷開連接,從而儘早終止發射,這對於無限或長期運行的觀察物至關重要。 我們將在本章結尾介紹Disposable。
三、冷觀察與熱觀察
根據Observable的實現方式,Observable和Observer之間的關係中存在細微的行爲。需要注意的是冷觀察者與熱觀察者的主要特徵,它定義了當存在多個觀察者時觀察者的行爲。首先,我們將介紹冷的Observable。
3.1、冷觀察
Cold Observables非常類似於可以向每個聽衆播放的音樂CD,因此每個人都可以隨時收聽所有曲目。以相同的方式,冷的Observables將向每個觀察者(Observer)重新發射,確保所有觀察者都獲得所有數據。大多數數據驅動的Observable都是冷的,其中包括Observable.just()和Observable.fromIterable()工廠。在下面的示例中,我們有兩個Observer訂閱了一個Observable。 Observable將首先向第一個Observer的發射所有發射,然後調用onComplete()。然後,它將再次向第二個Observer播放所有發射,並調用onComplete()。它們都通過獲取兩個單獨的流來接收相同的數據集,這是冷Observable的典型行爲:
import io.reactivex.Observable;
public class Ch2_11 {
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 1 Received: Alpha
Observer 1 Received: Beta
Observer 1 Received: Gamma
Observer 1 Received: Delta
Observer 1 Received: Epsilon
Observer 2 Received: Alpha
Observer 2 Received: Beta
Observer 2 Received: Gamma
Observer 2 Received: Delta
Observer 2 Received: Epsilon
即使第二個觀察者通過方法函數改變其發射量,它仍將獲得自己的發射量。 對冷的Observable使用諸如map()和filter()之類的運算符仍將保持所產生的Observable的冷性:
import io.reactivex.Observable;
public class Ch2_12 {
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
如前所述,發出有限數據集的可觀察源通常是冷的。
3.2、熱點觀察
您剛剛瞭解了冷的Observable,其工作原理類似於音樂CD。熱的Observable更像是電臺。它同時向所有Observer廣播相同的發射。如果觀察者Observer訂閱了熱的Observable,收到了一些發射量,然後又有另一個觀察者進來,則該第二觀察者將錯過這些發射量。就像廣播電臺一樣,如果調得太晚,您會錯過那首歌。從邏輯上講,熱的Observable通常表示事件,而不是有限的數據集。這些事件可以攜帶數據,但是有一個時間敏感的組件,後期的觀察者可能會錯過以前發出的數據,例如,JavaFX或Android UI事件可以表示爲熱門的Observable。在JavaFX中,可以使用Observable.create()在ToggleButton的selectedProperty()運算符旁邊創建Observable 。然後,您可以將布爾發射轉換爲指示ToggleButton是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 Ch2_13 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()工廠輕鬆地將其轉換爲RxJava Observable,以將ChangeListener掛鉤爲onNext()調用。每次單擊ToggleButton時,Observable 將發出true或false反映選擇狀態。這是一個簡單的示例,表明此Observable發出事件,但也發出true或false形式的數據。它將布爾值轉換爲字符串,並讓觀察者修改Label的文本。
在此JavaFX示例中,我們只有一個觀察者。如果我們要在發射發生後爲該ToggleButton的事件帶來更多觀察員,則那些新的觀察員將錯過這些發射。
JavaFX和Android上的UI事件是熱的Observable的主要示例,但是您也可以使用熱的Observable來反映服務器請求。如果您在實時Twitter流上創建了一個Observable,該Observable發出了某個主題的推文,那也將是一個熱門的Observable。所有這些來源可能都是無限的,儘管許多熱門的Observables確實是無限的,但它們不一定是無限的。他們只需要同時向所有觀察員共享發射,而不必爲遲到的觀察員重播錯過的發射。
請注意,由於我們從未處理過該JavaFX示例,因此確實留下了一個寬鬆的結局。在本章末尾介紹Disposables時,我們將再次進行討論。
3.3、ConnectableObservable
熱的Observable一種有用形式是ConnectableObservable。 即使是冷的,它也會採取任何可觀察的方法,並使其變熱,以便所有發射立即播放給所有觀察者。 要進行此轉換,您只需要在任何Observable上調用publish(),它將產生一個ConnectableObservable。 但是訂閱還不會開始發射。 您需要調用其connect()方法來開始發射。 這使您可以預先設置所有觀察者。 看一下以下代碼片段:
import io.reactivex.Observable;
import io.reactivex.observables.ConnectableObservable;
public class Ch2_14 {
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
請注意,一個觀察者是如何接收字符串的,而另一個觀察者是如何接收長度的,而兩個觀察者是如何以交錯方式打印它們的。 預先設置了兩個訂閱,然後調用connect()觸發發射。 而不是由觀察者1在觀察者2之前處理所有發射,而是將每個發射同時轉到每個觀察者。 觀察者1接收Alpha,觀察者2接收5,然後Beta和4,依此類推。 使用ConnectableObservable強制將每個發射同時發送到所有觀察者稱爲多播,我們將在第5章“多播”中詳細介紹。
ConnectableObservable有助於防止將數據重放到每個Observer。 如果重放發射成本很高,而您寧願立即將它們發射給所有觀察員,則可能要這樣做。 您也可以這樣做,即使下游有多個觀察員,也可以強制上游操作員使用單個流實例。 多個觀察者通常會導致上游有多個流實例,但是使用publish()返回ConnectableObservable會在publish()之前將所有上游操作合併爲一個流。 同樣,這些細微差別將在第5章“多播”中進行詳細介紹。現在,請記住ConnectableObservable很熱,因此,如果在connect()調用之後發生新的訂閱,它們將錯過以前觸發的發射。
四、其他Observable來源
我們已經介紹了一些工廠來創建Observable源,包括Observable.create(),Observable.just()和Observable.fromIterable()。 在繞過觀察者及其細微差別的繞道繞道之後,讓我們從中斷的地方開始,並介紹一些Observable工廠。
4.1、Observable.range()
要發出連續範圍的整數,可以使用Observable.range()。 這將從起始值發出每個數字,並遞增每次發出直到達到指定的計數。 這些數字都通過onNext()事件傳遞,然後通過onComplete()事件傳遞:
import io.reactivex.Observable;
public class Ch2_15 {
public static void main(String[] args) {
Observable.range(1, 10)
.subscribe(s -> System.out.println("RECEIVED: " + s));
}
}
輸出如下:
RECEIVED: 1
RECEIVED: 2
RECEIVED: 3
RECEIVED: 4
RECEIVED: 5
RECEIVED: 6
RECEIVED: 7
RECEIVED: 8
RECEIVED: 9
RECEIVED: 10
請密切注意,Observable.range()的兩個參數不是下限/上限。 第一個參數是起始值。 第二個參數是發射總量,其中將包括初始值和增量值。
嘗試發出Observable.range(5,10),您會注意到它發出5,然後是下一個連續的9個連續整數(總共發出10個):
import io.reactivex.Observable;
public class Ch2_16 {
public static void main(String[] args) {
Observable.range(5, 10)
.subscribe(s -> System.out.println("RECEIVED: " + s));
}
}
輸出如下:
RECEIVED: 5
RECEIVED: 6
RECEIVED: 7
RECEIVED: 8
RECEIVED: 9
RECEIVED: 10
RECEIVED: 11
RECEIVED: 12
RECEIVED: 13
RECEIVED: 14
請注意,如果需要發出更大的數字,則還有一個長等效項,稱爲Observable.rangeLong()。
4.2、Observable.interval()
如我們所見,可觀測對象具有隨着時間推移而發射的概念。 發射從源頭開始依次傳遞給觀察者。 但是這些發射物可以按時間間隔分配,具體取決於發射源何時提供。 我們的帶有ToggleButton的JavaFX示例演示了這一點,因爲每次單擊都會產生對或錯。
但是,讓我們來看一個使用Observable.interval()的基於時間的Observable的簡單示例。 它將在每個指定的時間間隔發出連續的長髮射(從0開始)。 在這裏,我們有一個Observable<Long>,它每秒發出一次:
import io.reactivex.Observable;
import java.util.concurrent.TimeUnit;
public class Ch2_17 {
public static void main(String[] args) {
Observable.interval(1, TimeUnit.SECONDS)
.subscribe(s -> System.out.println(s + " Mississippi"));
sleep(5000);
}
public static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Observable.interval()將以指定的間隔(在這種情況下爲1秒)無限發射。 但是,由於它在計時器上運行,因此它需要在單獨的線程上運行,並且默認情況下將在計算調度程序上運行。
我們將在第6章“併發性和並行化”中介紹併發性,並瞭解調度程序。 現在,僅需注意我們的main()方法將啓動此Observable,但它不會等待其完成。 現在它在單獨的線程上發出。 爲了防止我們的main()方法在Observable有機會觸發之前完成並退出應用程序,我們使用sleep()方法使該應用程序保持活動狀態五秒鐘。 在應用程序退出之前,這將使我們的Observable發出五秒鐘的時間。 創建生產應用程序時,您可能不會經常遇到此問題,因爲用於Web服務,Android應用程序或JavaFX等任務的非守護進程線程會使該應用程序保持活動狀態。
技巧問題:Observable.interval()返回的是熱還是冷Observable?由於它是事件驅動的(並且是無限的),因此您可能會傾向於說它是熱的。 但是在上面放一個觀察者,等待五秒鐘,然後添加另一個觀察者。 會怎麼樣呢? 讓我們來看看:
import io.reactivex.Observable;
import java.util.concurrent.TimeUnit;
public class Ch2_18 {
public static void main(String[] args) {
Observable<Long> seconds = Observable.interval(1,
TimeUnit.SECONDS);
//Observer 1
seconds.subscribe(l -> System.out.println("Observer 1: " + l));
//sleep 5 seconds
sleep(5000);
//Observer 2
seconds.subscribe(l -> System.out.println("Observer 2: " + l));
//sleep 5 seconds
sleep(5000);
}
public static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
輸出如下:
Observer 1: 0
Observer 1: 1
Observer 1: 2
Observer 1: 3
Observer 1: 4
Observer 1: 5
Observer 2: 0
Observer 1: 6
Observer 2: 1
Observer 1: 7
Observer 2: 2
Observer 1: 8
Observer 2: 3
Observer 1: 9
Observer 2: 4
看一下觀察者2進入五秒鐘後發生了什麼。請注意,它在自己的單獨計時器上,從0開始! 這兩個觀察者實際上正在獲得自己的發射,每個發射都從0開始。因此,這個Observable實際上是冷的。
要將所有觀察者置於具有相同發射率的同一計時器上,您將需要使用ConnectableObservable強制使這些發射變熱:
import io.reactivex.Observable;
import io.reactivex.observables.ConnectableObservable;
import java.util.concurrent.TimeUnit;
public class Ch2_19 {
public static void main(String[] args) {
ConnectableObservable<Long> seconds =
Observable.interval(1, TimeUnit.SECONDS).publish();
//observer 1
seconds.subscribe(l -> System.out.println("Observer 1: " + l));
seconds.connect();
//sleep 5 seconds
sleep(5000);
//observer 2
seconds.subscribe(l -> System.out.println("Observer 2: " + l));
//sleep 5 seconds
sleep(5000);
}
public static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
輸出如下:
Observer 1: 0
Observer 1: 1
Observer 1: 2
Observer 1: 3
Observer 1: 4
Observer 1: 5
Observer 2: 5
Observer 1: 6
Observer 2: 6
Observer 1: 7
Observer 2: 7
Observer 1: 8
Observer 2: 8
Observer 1: 9
Observer 2: 9
4.3、Observable.future()
RxJava Observables比Futures更健壯和更具表現力,但是如果您已有產生Futures的庫,則可以通過Observable.future()輕鬆將它們轉換爲Observables:
4.4、Observable.empty()
儘管這似乎似乎沒有用,但有時創建不發出任何內容並調用onComplete()的Observable有時會有所幫助:
import io.reactivex.Observable;
public class Ch2_20 {
public static void main(String[] args) {
Observable<String> empty = Observable.empty();
empty.subscribe(System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Done!"));
}
}
請注意,沒有發射內容被打印,因爲沒有發射。 它直接調用onComplete,它打印 Done! 觀察者中的消息。 空的可觀察值通常表示空的數據集。 當所有發射均不滿足條件時,它們也可能由諸如filter()之類的運算符產生。 有時,您會使用Observable.empty()故意創建空的Observable,而在本書中的某些地方我們都會看到一些示例。
空的Observable本質上是RxJava的null概念。 這是缺少值(從技術上講,是“值”)。 Empty Observables比null更優雅,因爲操作將簡單地繼續爲空而不是拋出NullPointerExceptions。 但是,當RxJava程序出現問題時,有時是因爲觀察者沒有收到任何輻射。 發生這種情況時,您必須跟蹤可觀察對象的操作員鏈,以找出導致發射變爲空的原因。
4.5、Observable.never()
Observable.never()很像Observable.empty()。 它們之間的唯一區別是,它從不調用onComplete(),永遠讓觀察者等待發射,但從不實際提供任何東西:
import io.reactivex.Observable;
public class Ch2_21 {
public static void main(String[] args) {
Observable<String> never = Observable.never();
never.subscribe(System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Done!"));
sleep(5000);
}
public static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
此Observable主要用於測試,在生產中並不經常使用。 我們必須像Observable.interval()一樣在這裏使用sleep(),因爲在啓動之後主線程不會等待它。 在這種情況下,我們只需使用sleep()五秒鐘即可證明沒有任何發射。 然後,該應用程序將退出。
4.6、Observable.error()
這也可能只是測試而已,但是您可以創建一個Observable,該對象立即使用指定的異常調用onError()
import io.reactivex.Observable;
public class Ch2_22 {
public static void main(String[] args) {
Observable.error(new Exception("Crash and burn!"))
.subscribe(i -> System.out.println("RECEIVED: " + i),
Throwable::printStackTrace,
() -> System.out.println("Done!"));
}
}
4.7、Observable.defer()
Observable.defer()是強大的工廠,因爲它能夠爲每個Observer創建單獨的狀態。 使用某些可觀察的工廠時,如果源是有狀態的,並且要爲每個觀察者創建一個單獨的狀態,則可能會遇到一些細微差別。 您的源Observable可能無法捕獲其參數發生變化的某些內容併發送過時的發射。 這是一個簡單的示例:我們有一個Observable.range(),它建立在兩個靜態int屬性上,即start和count。
如果您訂閱此Observable,請修改計數,然後再次訂閱,您將發現第二個Observer沒有看到此更改:
import io.reactivex.Observable;
public class Ch2_24 {
private static int start = 1;
private static int count = 5;
public static void main(String[] args) {
Observable<Integer> source = Observable.range(start, count);
source.subscribe(i -> System.out.println("Observer 1: " + i));
//modify count
count = 10;
source.subscribe(i -> System.out.println("Observer 2: " + i));
}
}
輸出如下:
Observer 1: 1
Observer 1: 2
Observer 1: 3
Observer 1: 4
Observer 1: 5
Observer 2: 1
Observer 2: 2
Observer 2: 3
Observer 2: 4
Observer 2: 5
要解決可觀察源無法捕獲狀態更改的問題,可以爲每個訂閱創建一個新的可觀察對象。 這可以使用Observable.defer()實現,該函數接受一個lambda指令,該lambda指示如何爲每個訂閱創建一個Observable。 因爲這每次都會創建一個新的Observable,所以它將反映驅動其參數的所有更改:
import io.reactivex.Observable;
class Ch2_25 {
private static int start = 1;
private static int count = 5;
public static void main(String[] args) {
Observable<Integer> source = Observable.defer(() ->
Observable.range(start, count));
source.subscribe(i -> System.out.println("Observer 1: " + i));
//modify count
count = 10;
source.subscribe(i -> System.out.println("Observer 2: " + i));
}
}
輸出如下:
Observer 1: 1
Observer 1: 2
Observer 1: 3
Observer 1: 4
Observer 1: 5
Observer 2: 1
Observer 2: 2
Observer 2: 3
Observer 2: 4
Observer 2: 5
Observer 2: 6
Observer 2: 7
Observer 2: 8
Observer 2: 9
Observer 2: 10
這是很好的! 當您的Observable源未捕獲對驅動它的事物的更改時,請嘗試將其放入Observable.defer()中。 如果您的Observable源代碼是天真的實現的,並且在多個Observer上表現不佳(例如,它重用了僅迭代一次數據的anIterator),那麼Observable.defer()也爲此提供了一種快速的解決方法。
4.8、Observable.fromCallable()
如果需要執行計算或操作然後發出它,則可以使用Observable.just()(或Single.just()或Maybe.just(),我們將在後面介紹)。 但有時,我們希望以懶惰或推遲的方式執行此操作。 另外,如果該過程引發錯誤,我們希望通過onError()在Observable鏈中發出該錯誤,而不是以傳統的Java方式在該位置將該錯誤引發。 例如,如果您嘗試將Observable.just()包裹在一個被1除以0的表達式周圍,則會拋出異常,而不是向Observer發出該異常:
import io.reactivex.Observable;
public class Ch2_26 {
public static void main(String[] args) {
Observable.just(1 / 0)
.subscribe(i -> System.out.println("RECEIVED: " + i),
e -> System.out.println("Error Captured: " + e));
}
}
輸出如下:
Exception in thread “main” java.lang.ArithmeticException: / by zero
如果我們要對錯誤處理做出反應,那麼這可能是不理想的。 也許您希望將錯誤沿着鏈條向下發送到觀察者,並在其中進行處理。 如果是這種情況,請改用Observable.fromCallable(),因爲它接受lambda Supplier ,並且將發出直到Observer發生的任何錯誤:
import io.reactivex.Observable;
public class Ch2_27 {
public static void main(String[] args) {
Observable.fromCallable(() -> 1 / 0)
.subscribe(i -> System.out.println("Received: " + i),
e -> System.out.println("Error Captured: " + e));
}
}
輸出如下:
Error Captured: java.lang.ArithmeticException: / by zero
這是很好的! 錯誤被髮送給觀察者,而不是被拋出。 如果初始化發射有可能引發錯誤,則應使用Observable.fromCallable()而不是Observable.just()。
五、Single,Completable和Maybe
有一些專門針對Observable的風格,它們是爲一個或一個發射明確設置的:Single,Maybe和Completable。 這些都緊密遵循Observable,並且應該直觀地用於您的反應式編碼工作流中。 您可以像創建Observable一樣創建它們(例如,它們每個都有自己的create()工廠),但是某些Observable運算符也可能返回它們。
5.1、Single
Single<T>本質上是一個Observable<T>,只會發出一項。 它的工作方式類似於Observable,但僅限於對單個發射有意義的運算符。 它還具有自己的SingleObserver接口:
public interface SingleObserver<T> {
void onSubscribe(@NonNull Disposable var1);
void onSuccess(@NonNull T var1);
void onError(@NonNull Throwable var1);
}
onSuccess()本質上將onNext()和onComplete()合併爲一個事件,該事件接受一個發射。 當您針對Single調用subscribe()時,您需要爲onSuccess()和可選的onError()提供lambda:
import io.reactivex.Single;
public class Ch2_28 {
public static void main(String[] args) {
Single.just("Hello")
.map(String::length)
.subscribe(System.out::println,
Throwable::printStackTrace);
}
}
某些RxJava Observable運算符將產生Single,我們將在下一章中看到。例如,first()運算符將返回Single,因爲該運算符在邏輯上涉及單個項目。 但是,如果Observable輸出爲空,它將接受默認值作爲參數(在以下示例中將其指定爲Nil):
import io.reactivex.Observable;
public class Ch2_29 {
public static void main(String[] args) {
Observable<String> source =
Observable.just("Alpha", "Beta", "Gamma");
source.first("Nil") //returns a Single
.subscribe(System.out::println);
}
}
輸出如下:Alpha
Single必須有一個發射,如果您只提供一個發射,則應該選擇它。 這意味着您應該嘗試使用Single.just(“ Alpha”)而不是Observable.just(“ Alpha”)。 Single上有一些運算符,您可以在需要時將其轉換爲Observable,例如toObservable()。
如果發射爲0或1,則需要使用Maybe。
5.2、Maybe
Maybe就像是Single一樣,除了它根本不允許發射。MaybeObserver很像標準的Observer,但是onNext()被稱爲onSuccess():
public interface MaybeObserver<T> {
void onSubscribe(@NonNull Disposable var1);
void onSuccess(@NonNull T var1);
void onError(@NonNull Throwable var1);
void onComplete();
}
給定的Maybe 將僅發射0或1個發射。 它將可能的發射傳遞給onSuccess(),無論哪種情況,完成後都會調用onComplete()。 Maybe.just()可用於創建可能發射單個項目的Maybe。 Maybe.empty()將創建不產生任何發射的Maybe:
import io.reactivex.Maybe;
public class Ch2_30 {
public static void main(String[] args) {
// has emission
Maybe<Integer> presentSource = Maybe.just(100);
presentSource.subscribe(s -> System.out.println("Process 1 received:" + s),
Throwable::printStackTrace,
() -> System.out.println("Process 1 done!"));
//no emission
Maybe<Integer> emptySource = Maybe.empty();
emptySource.subscribe(s -> System.out.println("Process 2 received:" + s),
Throwable::printStackTrace,
() -> System.out.println("Process 2 done!"));
}
}
輸出如下:
Process 1 received: 100
Process 2 done!
我們稍後將學習的某些Observable運算符產生的Maybe。 一個示例是firstElement()運算符,它與first()相似,但是如果沒有發出任何元素,它將返回空結果:
import io.reactivex.Observable;
public class Ch2_31 {
public static void main(String[] args) {
Observable<String> source =
Observable.just("Alpha", "Beta", "Gamma", "Delta", "Epsilon");
source.firstElement().subscribe(
s -> System.out.println("RECEIVED " + s),
Throwable::printStackTrace,
() -> System.out.println("Done!"));
}
}
輸出如下:RECEIVED Alpha
5.3、Completable
Completable僅與正在執行的動作有關,但不接收任何發射。 從邏輯上講,它沒有onNext()或onSuccess()來接收發射,但是它確實具有onError()和onComplete():
public interface CompletableObserver {
void onSubscribe(@NonNull Disposable var1);
void onComplete();
void onError(@NonNull Throwable var1);
}
Completable是您可能不會經常使用的東西。 您可以通過調用Completable.complete()或Completable.fromRunnable()快速構建一個。 前者將不做任何事情立即調用onComplete(),而fromRunnable()將在調用onComplete()之前執行指定的操作:
import io.reactivex.Completable;
public class Ch2_32 {
public static void main(String[] args) {
Completable.fromRunnable(() -> runProcess())
.subscribe(() -> System.out.println("Done!"));
}
public static void runProcess() {
//run process here
}
}
輸出如下:Done!
六、Disposing(處置)
當您對一個Observable subscribe()來接收發射時,將創建一個流以通過Observable鏈處理這些發射。當然,這會佔用資源。完成後,我們希望處置這些資源,以便可以對其進行垃圾回收。幸運的是,有限的調用onComplete(),Observable通常會在完成時安全地自行處置。但是,如果您使用的是無限或長期運行的Observable,則可能會遇到想要顯式停止發射並處置與該訂閱關聯的所有內容的情況。實際上,您不能信任垃圾收集器來處理不再需要的活動訂閱,並且有必要進行顯式處理以防止內存泄漏。Disposable是Observable和活動Observer之間的鏈接,您可以調用其dispose()方法來停止發射並處置用於該Observer的所有資源。它還具有isDisposed()方法,指示是否已將其丟棄:
public interface Disposable {
void dispose();
boolean isDisposed();
}
當提供onNext(),onComplete()和/或onError()lambda作爲subscribe()方法的參數時,它實際上將返回Disposable。 您可以隨時通過調用其dispose()方法來停止發射。 例如,我們可以在五秒鐘後停止接收來自Observable.interval()的發射:
import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
import java.util.concurrent.TimeUnit;
public class Ch2_33 {
public static void main(String[] args) {
Observable<Long> seconds =
Observable.interval(1, TimeUnit.SECONDS);
Disposable disposable =
seconds.subscribe(l -> System.out.println("Received: " + l));
//sleep 5 seconds
sleep(5000);
//處理和停止發射
disposable.dispose();
//將沒有更多的發射
sleep(5000);
}
public static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在這裏,我們讓Observable.interval()與一個Observer一起運行五秒鐘,但是我們保存了subscribe()方法返回的Disposable。 然後,我們調用Disposable的dispose()方法來停止該進程並釋放所有正在使用的資源。 然後,我們再睡五秒鐘,以證明不再有發射發生。
6.1、在觀察者內處理Disposable
之前,我回避談論Observer中的onSubscribe()方法,但是現在我們將解決它。 您可能已經注意到,在Observer的實現中通過onSubscribe()方法傳遞了Disposable。 此方法在RxJava 2.0中已添加,它使Observer可以隨時處理預訂。
例如,您可以實現自己的Observer並使用onNext(),onComplete()或onError()來訪問Disposable。這樣,如果出於某種原因不想再發射了,這三個事件都可以調用dispose():
Disposable從源頭一直髮送到觀察者鏈,因此Observable鏈中的每個步驟都可以訪問Disposable。
請注意,將Observer傳遞給subscribe()方法將是無效的,並且不會返回Disposable,因爲Observer可能處理它。 如果您不想明確地處理Disposable並希望RxJava爲您處理它(在您有控制權之前,這可能是一個好主意),則可以將ResourceObserver擴展爲Observer,它使用默認的Disposable處理。 將其傳遞給subscribeWith()而不是subscribe(),您將獲得默認的Disposable返回:
import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
import io.reactivex.observers.ResourceObserver;
import java.util.concurrent.TimeUnit;
public class Ch2_34 {
public static void main(String[] args) {
Observable<Long> source =
Observable.interval(1, TimeUnit.SECONDS);
ResourceObserver<Long> myObserver = new
ResourceObserver<Long>() {
@Override
public void onNext(Long value) {
System.out.println(value);
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
System.out.println("Done!");
}
};
//capture Disposable
Disposable disposable = source.subscribeWith(myObserver);
}
}
6.2、使用CompositeDisposable
如果您需要管理和處置多個訂閱,則使用CompositeDisposable會有所幫助。 它實現了Disposable,但在內部保留了一組Disposable,您可以將這些Disposable添加到其中,然後立即將其全部處置:
import io.reactivex.Observable;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import java.util.concurrent.TimeUnit;
public class Ch2_35 {
private static final CompositeDisposable disposables
= new CompositeDisposable();
public static void main(String[] args) {
Observable<Long> seconds =
Observable.interval(1, TimeUnit.SECONDS);
//subscribe and capture disposables
Disposable disposable1 =
seconds.subscribe(l -> System.out.println("Observer 1: " +
l));
Disposable disposable2 =
seconds.subscribe(l -> System.out.println("Observer 2: " +
l));
//put both disposables into CompositeDisposable
disposables.addAll(disposable1, disposable2);
//sleep 5 seconds
sleep(5000);
//dispose all disposables
disposables.dispose();
//sleep 5 seconds to prove
//there are no more emissions
sleep(5000);
}
public static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
CompositeDisposable是一個簡單但有用的實用程序,用於維護可以通過調用add()或addAll()添加到Disposable的集合。 當您不再需要這些訂閱時,可以調用dispose()一次處理所有訂閱。
6.3、使用Observable.create()處理Disposable
如果您的Observable.create()返回一個長期運行或無限的Observable,則理想情況下,應定期檢查ObservableEmitter的isDisposed()方法,以查看是否應繼續發送發射。 如果訂閱不再有效,這可以避免不必要的工作。在這種情況下,您應該使用Observable.range(),但是爲了示例起見,假設我們在Observable.create()的for循環中發出整數。 發出每個整數之前,應確保ObservableEmitter不指示已調用處置:
import io.reactivex.Observable;
public class Ch2_36 {
public static void main(String[] args) {
Observable<Integer> source =
Observable.create(observableEmitter -> {
try {
for (int i = 0; i < 1000; i++) {
while (!observableEmitter.isDisposed()) {
observableEmitter.onNext(i);
}
if (observableEmitter.isDisposed())
return;
}
observableEmitter.onComplete();
} catch (Throwable e) {
observableEmitter.onError(e);
}
});
}
}
如果Observable.create()包裹在某些資源周圍,則還應該處理該資源以防止泄漏。 ObservableEmitter具有setCancellable()和setDisposable()方法。 在較早的JavaFX示例中,發生處置時,應從JavaFX ObservableValue中刪除ChangeListener。 我們可以爲setCancellable()提供一個lambda,它將爲我們執行以下操作,該操作將在調用dispose()時發生:
七、總結
這是一章很緊湊的章節,但是當您學習如何使用RxJava處理實際工作時,它將提供堅實的基礎。 RxJava具有所有表現力,它們的細微差別完全是由於其所要求的思維方式的改變。 它採用了像Java這樣的命令性語言並將其改編爲響應式和功能性,從而完成了大量令人印象深刻的工作。 但是,這種互操作性需要對Observable和Observer之間的實現有一些瞭解。 我們通過各種方式來創建Observable以及它們與Observer的交互方式。
花些時間嘗試消化所有這些信息,但不要讓它阻止您繼續進行下一兩章,在此,RxJava的實用性開始形成。 在隨後的各章中,RxJava的實用實用性將開始變得清晰。