第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)上看到運算符的完整列表。

信不信由你,我們纔剛剛開始。 本章僅介紹基本運算符。 在接下來的章節中,我們將介紹執行強大行爲(例如併發和多播)的運算符。 但是在此之前,讓我們繼續結合可觀察對象的運算符。

發佈了32 篇原創文章 · 獲贊 10 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章