RxJava常用操作符官方文件翻譯及Kotlin示例(1)

Starwars發表於2019-02-26

Rxjava2 可謂是日常開發中的利器,特別是在非同步任務中更能發揮作用。響應式程式設計以及流式api的良好支援,給予了更好的編碼體驗。越來越多開發者漸漸用起來了。學習rxjava2最好的地方無外乎官方文件,詳細且完整。以下結合官方文件和我自己的理解以及例子,解釋各個操作符的用法,給各位以及我自己作一篇參考。

怎麼用Rxjava2

要使用RxJava,需要先建立Observables(發出資料項),以各種方式轉換這些Observable以獲取所需要的精確資料項(通過使用Observable運算子),然後觀察並響應這些需要的專案序列(通過實現觀察者)
或者訂閱者,然後將它們訂閱到最終的變換後的Observables)。

Creating Observables 建立操作符

just

通過獲取預先存在的物件並在訂閱時將該特定物件釋出給下游使用者來構造反應型別。為方便起見,存在2到9個引數的過載,這些物件(具有相同的常見型別)將按指定的順序發出。就像From類似,但請注意From將傳入一個陣列或一個iterable或類似的東西來取出要發出的專案,而Just只是簡單地發出陣列或者迭代器。

請注意,如果將null傳遞給Just,它將返回一個Observable,它將null作為項發出。不要錯誤地假設這將返回一個空的Observable(一個根本不發出任何專案)。為此,需要使用Empty運算子。

just
    fun testOpJust() {
        val arr = arrayOf("mary", "tom", "ben", "lisa", "ken")
        Observable.fromArray(arr).filter { it.size > 3 }.map { it + "s" }.subscribe(System.out::println)

        val list = arrayListOf("mary", "tom", "ben", "lisa", "ken")
        Observable.just(list).forEach { it -> System.out.println(it + "s") }

        list.stream().filter { it -> it.length > 3 }.map { "$it s" }.forEach(System.out::println)
    }
複製程式碼

from

根據預先存在的源或生成器型別構造序列。當使用Observable時,如果使用的所有資料都可以表示為Observables,而不是Observables和其他型別的混合,則可以更方便。這允許使用一組運算子來控制資料流的整個生命週期。例如,Iterables可以被認為是一種的Observable;作為一種始終只發出單一專案的Observable。通過將這些物件顯式轉換為Observable,可以將它們作為對等體與其他Observable進行互動。因此,大多數ReactiveX實現都具有允許將特定於語言的物件和資料結構轉換為Observable的方法。

注意:這些靜態方法使用字尾命名約定(即,在方法名稱中重複引數型別)以避免過載解析模糊。

from
fromIterable

從java.lang.Iterable源(例如Lists,Sets或Collections或custom Iterables)發出訊號,然後完成序列。

可用於 Flowable ,Observable

fromArray

發訊號通知給定陣列的元素,然後完成序列。
可用於Flowable,Observable

注意:RxJava不支援原始陣列,只支援(通用)引用陣列。

fun testOpFrom(){
        val list = arrayListOf<Int>(1,2,3,4,5,6)
        Observable.fromIterable(list).subscribe(System.out::println)

        Observable.fromArray(1,2,3,4,5,6).subscribe(System.out::println)

    }
複製程式碼
fromCallable

當消費者訂閱時,呼叫給定的java.util.concurrent.Callable並將其返回值(或丟擲的異常)轉發給該使用者。

可用於:Observable,Flowable,Maybe,Single,Completable

備註:在Completable中,忽略實際返回值,並且Completable完成。

       Observable.fromCallable<String> {
            "hello"
        }.subscribe(System.out::println)

        Completable.fromCallable{
            "complatable from callable"
        }.subscribe {
            System.out.println("complete")
        }
複製程式碼

fromAction

當消費者訂閱時,呼叫給定的io.reactivex.function.Action並且消費者完成或接收Action丟擲的異常。

可用於: Maybe,Completable

   Maybe.fromAction<String>{
            System.out.println("maybe from action")
        }.subscribe(System.out::println)
複製程式碼

以下標星先不多做解釋,用得不多

*fromRunnable

*fromFuture

*from{reactive type}

將另一種反應型別包裹或轉換為目標反應型別。具有以下簽名模式的各種反應型別中提供以下組合:targetType.from {sourceType}()

*注意:並非所有可能的轉換都是通過from {reactive type}方法系列實現的。檢視to {reactive type}方法系列以獲得進一步的轉換可能性。

注意:fromAction和fromRunnable之間的區別在於Action介面允許丟擲已受檢的異常,而java.lang.Runnable則不然。

error

可用於Observable,Flowable,Maybe,Single,Completable

通過java.util.concurrent.Callable向消費者發出預先存在或生成的錯誤訊號。

  fun testOpError(){
        Observable.error<Throwable>(IOException(""))
                .subscribe({
                    System.out.print("不會列印吧")
                },{
                    it.printStackTrace()
                },{
                    System.out.println("也不會列印")
                })
    }
複製程式碼

一個典型的用例是使用onErrorResumeNext有條件地對映或抑制鏈中的異常:

   /**
     * 抑制鏈上發生的異常
     */
    @Test
    fun testOpOnErrorResumeNext() {
        val observable = Observable.fromCallable {
            if (Math.random() < 0.5f) {
                throw IllegalArgumentException()
            }
            throw IOException()
        }

        observable.onErrorResumeNext(Function {
            if (it is IllegalArgumentException) {
                Observable.empty()
            } else {
                Observable.error(it)
            }
        }).subscribe({
            System.out.println("nothing")
        },{
            it.printStackTrace()
        },{
            System.out.println("empty")
        })
    }
複製程式碼

這個onErrorResumeNext 厲害了,可以說之前一直不太明白怎麼很好的處理。通過此操作符可以抑制錯誤的傳遞,本來如果subscribe發生了錯誤會觸發onError回撥。事實上可能發生了錯誤,需要不處理或者抑制產生。在onErrorResumeNext的function引數中,可以根據錯誤型別返回處理流程。

  • empty 這種型別的源在訂閱後立即表示完成。
    可用於Observable,Flowable,Maybe,Single,Completable

示例可見onErrorResumeNext的例子

empty

empty傳送直接表示完成,就是訂閱者直接呼叫onComplete回撥。onNext 不會執行

  • never 這種型別的源不會發出任何onNext,onSuccess,onError或onComplete的訊號。這種型別的反應源可用於測試或“禁用”組合子操作符中的某些源。

可用於Observable,Flowable,Maybe,Single,Completable

不會對訂閱者的任何回撥進行呼叫。禁用也可理解,比如傳送了錯誤,都不往下執行

  • interval 定期生成無限的,不斷增加的數字(Long型別)。intervalRange變體生成有限數量的此類數字。

可用於Observable,Flowable

interval
    fun testOpInterval(){
        Observable.interval(1,TimeUnit.SECONDS)
                .onErrorResumeNext(Function { 
                    Observable.error(it)
                })
                .subscribe({
                    if (it.rem(5) == 0L) {
                        System.out.println("tick")
                    } else {
                        System.out.println("tock")
                    }
                },{
                    it.printStackTrace()
                },{
                    System.out.println("interval complete")
                })
    }
複製程式碼
  • Timer運算子建立一個Observable,在指定的一段時間後發出一個特定項。
    Timer

也就是說在給定的時間之後傳送事件

  • range 為每個消費者生成一系列值。range()方法生成Integers,rangeLong()生成Longs。Range運算子按順序發出一系列順序整數,您可以在其中選擇範圍的起點及其長度。

可用於 Observable,Flowable

range
    fun testOpRange(){
        val s = "test range operation now"
        Observable.range(0,s.length- 3)
                .map { "${s[it]} in range"}
                .subscribe {
                    System.out.println(it)
                }
    }
複製程式碼

發出一系列值,引數為起點,和長度。

  • generate 建立一個冷,同步和有狀態的值生成器。

可用於Observable,Flowable

create
   @Test
    fun testOpGenerate(){
        val start = 1
        val increaseValue = 2
        Observable.generate<Int,Int>(Callable<Int> {
            start
        }, BiFunction<Int, Emitter<Int>,Int> {
            t1, t2 ->
            t2.onNext(t1 + increaseValue)
            t1 + increaseValue
        }).subscribe {
            System.out.println("generate value : $it")
        }
    }
複製程式碼

不太明白乾啥的,具體應用場景。只是一直不間斷的產生值

Filtering Observables 過濾Observable

過濾操作是非常常用且重要的,而且相關的操作符也很多

Debounce

可用於Observable,Flowable

刪除響應源發出的專案,在給定的超時值到期之前,這些專案後面跟著更新的專案。計時器重置每次發射。此運算子會跟蹤最近發出的專案,並且僅在有足夠的時間過去而沒有源發出任何其他專案時才會發出此專案。

按照我得理解就是debounde傳入了超時值,在該時間之內如果多次發射,取離超時值最近得值。既然又超時那麼也應該又開始時間,開始時間就是一組發射最開始值得時間,這一組發射得值的時的差是在debounce超時時間之內。

// Diagram:
// -A--------------B----C-D-------------------E-|---->
//  a---------1s
//                 b---------1s
//                      c---------1s
//                        d---------1s
//                                            e-|---->
// -----------A---------------------D-----------E-|-->

   fun testOpDebounce(){
        Observable.create<String>{
            it.onNext("A")
            Thread.sleep(1_500)
            it.onNext("B")
            Thread.sleep(500)
            it.onNext("C")
            Thread.sleep(250)
            it.onNext("D")
            Thread.sleep(2_000)
            it.onNext("E")
        }.debounce(1,TimeUnit.SECONDS)
                .subscribe(System.out::println)
    }
複製程式碼

distinct

可用於Observable Flowable
通過僅發出與先前專案相比不同的專案來過濾反應源。可以指定io.reactivex.functions.Function,將源發出的每個專案對映到一個新值中,該值將用於與先前的對映值進行比較。Distinct運算子通過僅允許尚未發出的專案來過濾Observable。在一些實現中,存在允許調整兩個項被視為“不同”的標準的變體。在一些實施例中,存在操作符的變體,其僅將專案與其前一個專案進行比較以獲得更精確的比較,從而僅過濾連續的重複專案,序列中的專案。

    fun testOpDistinct(){
        Observable.fromArray(1,2,3,3,4,5)
                .distinct()
                .subscribe(System.out::println)

        // 用來過濾序列中一組值前後是否相同得值
        Observable.fromArray(1,1,2,3,2)
                .distinct { "呵呵" }
                .subscribe(System.out::println)
    }
複製程式碼

過載的方法,傳入keySelectro ,作用是對每個元素應用方法得到得新得值,再決定怎麼去重

distinctUntilChanged

可用於Observable Flowable
通過僅發出與其前一個元素相比較不同的專案來過濾反應源。可以指定io.reactivex.functions.Function,將源發出的每個專案對映到一個新值中,該值將用於與先前的對映值進行比較。或者,可以指定io.reactivex.functions.BiPredicate作為比較器函式來比較前一個。

        Observable.fromArray(1,2,3,3,4,5)
//                .distinctUntilChanged()
                .distinctUntilChanged { t1, t2 ->
                    t1 == t2
                }
                .subscribe(System.out::println)
複製程式碼

可以說是distinct的加強版,多了一個可以傳入比較器的過載方法

elementAt

課用於Flowable,Observable
在來自反應源的一系列發射的資料項中,以指定的從零開始的索引發出單個專案。如果指定的索引不在序列中,則可以指定將發出的預設項。

簡單說就是按照發出項的次序獲取指定的位置的元素

     Observable.fromArray(1,2,3,3,4,5)
                .elementAt(2)
                .subscribe(System.out::println)
複製程式碼

elementAtOrError

filter

可用於Observable,Flowable,Maybe,Single
通過僅發出滿足指定函式的項來過濾由反應源發出的項。

過濾偶數
 Observable.fromArray(1,2,3,3,4,5)
                .filter {
                    it.rem(2) == 0
                }
                .subscribe(System.out::println)}
複製程式碼

first

可用於Flowable,Observable
僅發出反應源發出的第一個專案,或者如果源完成而不發出專案則發出給定的預設專案。這與firstElement的不同之處在於此運算子返回Single,而firstElement返回Maybe。

   Observable.fromArray(1,2,3,3,4,5)
                .first(-1)
                .subscribe(Consumer<Int> {
                    System.out.println("onNext :$it")
                })
                
                      Observable.fromArray(1,2,3,3,4,5)
                .firstElement()
                .subscribe {
                    System.out.println("onNext :$it")
                }
複製程式碼

firstOrError

僅發出響應源發出的第一個專案,或者如果源完成而不發出專案則發出java.util.NoSuchElementException訊號。

ignoreElement

可用於Maybe Single
忽略Single或Maybe源發出的單個專案,並返回一個Completable,它僅從源中發出錯誤或完成事件的訊號。

ignoreElement
 Maybe.timer(1L,TimeUnit.SECONDS)
                .ignoreElement()
                .doOnComplete {
                    System.out.println("done")
                }
                .blockingAwait()
複製程式碼

ignoreElements

忽略Single或Maybe源發出的單個專案,並返回一個Completable,它僅從源中發出錯誤或完成事件的訊號。

 Observable.timer(1L,TimeUnit.SECONDS)
                .ignoreElements()
                .doOnComplete {
                    System.out.println("completed")
                }
                .blockingAwait()
複製程式碼

last

可用於Observable,Flowable

僅發出反應源發出的最後一個專案,或者如果源完成而不發出專案則發出給定的預設專案。這與lastElement的不同之處在於此運算子返回Single,而lastElement返回Maybe。

   Observable.fromArray(1,2,3,3,4,5)
                .last(-1)
                .subscribe(Consumer<Int>{
                    System.out.println("last $it")
                })
複製程式碼

lastElement

  Observable.fromArray(1,2,3,3,4,5)
                .lastElement()
                .subscribe(Consumer<Int>{
                    System.out.println("last $it")
                })
複製程式碼

lastOnError

僅發出響應源發出的最後一項,或者如果源完成而不發出項,則發出java.util.NoSuchElementException訊號。

ofType

可用於Flowable,Observable,Maybe
通過僅發出指定型別的專案來過濾反應源發出的專案。

 Observable.fromArray(1,2.1f,3,3,4,5)
                .ofType(Int::class.java)
                .subscribe(Consumer<Int>{
                    System.out.println("last $it")
                })
複製程式碼

sample

可用於Observable Flowable
通過僅在週期性時間間隔內發出最近發出的專案來過濾反應源發出的專案。


 Observable.create<String> {
            it.onNext("A")
            Thread.sleep(1_000)

            it.onNext("B")
            Thread.sleep(300)

            it.onNext("C")
            Thread.sleep(700)

            it.onNext("D")
            it.onComplete()
        }.sample(1,TimeUnit.SECONDS)
                .blockingSubscribe(System.out::println)
複製程式碼

skip

刪除響應源發出的前n個專案,併發出剩餘專案。您可以通過使用Skip運算子修改Observable來忽略Observable發出的前n個專案,並僅參加之後的專案。

 Observable.fromArray("hehe",2.1f,3,3,4,5)
//                .ofType(String::class.java)
                .skip(3)
                .subscribe {
                    System.out.println(it)
                }
複製程式碼

skipLast

丟棄反應源發出的最後n個專案,併發出剩餘的專案。

take

可用於Flowable Observable
僅發出反應源發出的前n項。

     Observable.fromArray("hehe",2.1f,3,3,4,5)
                .take(2)
                .subscribe(System.out::println)
複製程式碼

takeLast

可用於Flowable Observable
僅發出反應源發出的最後n個專案。

throttleFirst

可用於Flowable Observable

跟debounce有些相似,是取時間範圍內第一個,在點選事件過濾很常用

在指定持續時間的連續時間視窗期間僅發出由反應源發出的第一個專案。

 Observable.create<String> {
            it.onNext("A")
            Thread.sleep(300)

            it.onNext("B")
            Thread.sleep(400)
        }.throttleFirst(1,TimeUnit.SECONDS)
                .subscribe(System.out::println)
複製程式碼

throttleLast

可用於Observable,Flowable
在指定持續時間的連續時間期間僅發出由反應源發出的最後一個專案。跟throttleFirst相反,取最後一個值

throttleWithTimeout

跟debounce的別名

    public final Observable<T> throttleWithTimeout(long timeout, TimeUnit unit) {
        return debounce(timeout, unit);
    }
複製程式碼

timeout

從Observable或Flowable源發出專案,但如果在從上一項開始的指定超時持續時間內未發出下一項,則以java.util.concurrent.TimeoutException終止。對於Maybe,Single和Completable,指定的超時持續時間指定等待成功或完成事件到達的最長時間。如果Maybe,Single或Completable在給定時間內沒有完成,將發出java.util.concurrent.TimeoutException。

   Observable.create<String>{
            it.onNext("A")
            Thread.sleep(600)

            it.onNext("B")
            Thread.sleep(1_500)

            it.onNext("C")
            Thread.sleep(500)
        }.subscribeOn(Schedulers.io())
                .subscribe({
                    System.out.println(it)
                },{
                    it.printStackTrace()
                })
複製程式碼

捕獲處理

一下為Kotlin編寫的程式碼,可以看到在發生錯誤的情況下,通過onError() 丟擲了錯誤,並且需要在訂閱者,第二個引數傳入,處理錯誤的回撥。

    fun testErrorHandle() {
        Observable.create<String> {
            it.onNext("start")
            Thread {
                try {
                    System.out.println("start open ...")
                    it.onNext("start open ...")
                    val stream = URL("https://www.baidu.com").openStream()
                    System.out.println("after url ...")
                    it.onNext("after url")
                    val br = stream.bufferedReader()
                    if (!it.isDisposed) {
                        var text = br.readText()
                        it.onNext(text)
                    }
                    stream.close()
                    br.close()
                    it.onNext("after open ...")
                    if (!it.isDisposed) {
                        it.onComplete()
                    }
                }catch (e : java.lang.Exception) {
                    System.out.println(e)
                    e.printStackTrace()
                    it.onError(e)
                }
            }.start()
        }.subscribe(System.out::println) {
            it.printStackTrace()
            System.out.println("what the fuck")
        }
    }
複製程式碼

Observable通常不會丟擲異常。相反,它會通過使用onError通知終止Observable序列來通知任何觀察者發生了不可恢復的錯誤。

這有一些例外。例如,如果onError()呼叫本身失敗,Observable將不會嘗試通過再次呼叫onError來通知觀察者,但會丟擲RuntimeException,OnErrorFailedException或OnErrorNotImplementedException。

從onError通知中恢復的技術

因此,不是捕獲異常,而是觀察者或操作者應該更通常地響應異常的onError通知。還有各種Observable運算子可用於對來自Observable的onError通知作出反應或從中恢復。例如,可以使用運算子:

  1. 吞下錯誤並切換到備份Observable以繼續序列
  2. 吞下錯誤併發出預設項
  3. 吞下錯誤並立即嘗試重啟失敗的Observable
  4. 吞下錯誤並嘗試在一些退避間隔後重新啟動失敗的Observable

可以使用錯誤處理運算子中描述的運算子來實現這些策略。

吞下的意思,應該是不處理異常

RxJava特定的異常以及如何處理它們

CompositeException
這表明發生了多個異常。可以使用異常的getExceptions()方法來檢索構成組合的各個異常。

MissingBackpressureException
這表示試圖將過多發出資料項應用於它的Observable。有關背壓(github.com/ReactiveX/R…)的Observable的解決方法,請參閱Backpressure。

OnErrorFailedException
這表明Observable試圖呼叫其觀察者的onError()方法,但該方法本身引發了異常。

OnErrorNotImplementedException
這表明Observable試圖呼叫其觀察者的onError()方法,但是沒有這樣的方法存在。可以通過修復Observable以使其不再達到錯誤條件,通過在觀察者中實現onError處理程式,或通過使用本頁其他地方描述的其中一個運算子到達觀察者之前截獲onError通知來消除此問題。。

OnErrorThrowable
觀察者將這種型別的throwable傳遞給他們的觀察者的onError()處理程式。此變數的Throwable包含有關錯誤的更多資訊以及錯誤發生時系統的Observable特定狀態,而不是標準Throwable。

參考資料

官網文件

相關文章