RxJava 合併操作

PandaQ發表於2020-04-06

在專案開發中常常會在一個頁面中執行多個任務,多執行緒非同步執行任務時哪個任務先結束出結果這些並不好控制,譬如要進行幾個併發的網路請求在都拿到結果後需要對資料進行處理,希望的是使用者只感知一次資料載入,如果不能做到這樣,反饋到使用者介面的體驗也就不好了。通過 RxJava 的合併操作符我們能夠很方便的應對這些情況。

常用的幾個合併操作符

我們先建立如下三個不同資料型別的 observable 作為示例資料

	// 先建立三個不同型別的示例 observable
    private val observable = Observable.fromArray(1, 2, 3)
            .concatMap(object : Function<Int, ObservableSource<Int>> {
                override fun apply(t: Int): ObservableSource<Int> {
                    return Observable.just(t).delay(1000, TimeUnit.MILLISECONDS)
                }
            })

    private val observable1 = Observable.just("a", "b", "c")
            .concatMap(object : Function<String, ObservableSource<String>> {
                override fun apply(t: String): ObservableSource<String> {
                    return Observable.just(t).delay(400, TimeUnit.MILLISECONDS)
                }
            })
			
	private val observable2 = Observable.fromArray(1.0f, 2.0f, 3.0f)
            .concatMap(object : Function<Float, ObservableSource<Float>> {
                override fun apply(t: Float): ObservableSource<Float> {
                    return Observable.just(t).delay(1000, TimeUnit.MILLISECONDS)
                }
            })
複製程式碼

concat()

concat() 操作符將多個 Observable 按先後順序進行合併,當合並的 Observable 泛型型別不一致時,事件流中的物件型別只能使用 Object(java),Any(kotlin)。通過如下示例程式碼可以直觀的看到 concat 後事件的傳送順序:

        Observable.concat(observable, observable1)
                .subscribe(object : AppCallBack<Any>() {
                    override fun success(data: Any?) {
                        println("contact----->$data")
                    }

                    override fun fail(code: Long?, msg: String?) {

                    }

                    override fun finish(success: Boolean) {
                        merge()
                    }

                })
複製程式碼

得到的輸出結果為:

concat.png
observable 傳送 1、2、3 的時間間隔是大於 observable1傳送 a、b、c 的間隔的,但輸出結果為 123 列印完畢後再列印 abc 。這說明 concat 操作符是線性有序的,要等前一個 observable 傳送完畢後才會處理下一個 observable。當我們需要多個介面的返回資料按順序進行處理時可以使用 concat 操作符合並請求。

多於四個 observable 的合併: 兩種方式

  • concat() 傳入一個 Iterable<? extends ObservableSource<? extends T> 物件
  • concatArray() 傳入一個 Observable 陣列。

merge()

merge() 操作符將多個 Observable 無序合併,當合並的 Observable 泛型型別不一致時,事件流中的物件型別只能使用 Object(java),Any(kotlin)。

        Observable.merge(observable, observable1)
                .subscribe(object : AppCallBack<Any>() {
                    override fun success(data: Any?) {
                        println("merge----->$data")
                    }

                    override fun fail(code: Long?, msg: String?) {

                    }

                    override fun finish(success: Boolean) {
                        zip()
                    }

                })
複製程式碼

得到的輸出結果為:

merge.png
abc 與 123 是按照時間先後順序交錯進行輸出的,說明 merge() 後事件的傳送是併發的無序的,先傳送先處理

多於四個 observable 的合併: 兩種方式

  • merge() 傳入一個 Iterable<? extends ObservableSource<? extends T> 物件
  • mergeArray() 傳入一個 Observable 陣列。

zip()

上面的兩種合併都是單個事件 item 訂閱監聽,如果想合併的事件 item 都接收到資料時處理這兩個事件資料就需要使用 zip() 操作符。

        Observable.zip(observable, observable1, object : BiFunction<Int, String, String> {
            override fun apply(t1: Int, t2: String): String {
                return "t1=$t1  t2=$t2"
            }
        }).subscribe(object : AppCallBack<String>() {
            override fun success(data: String?) {
                println("zip----->$data")
            }

            override fun fail(code: Long?, msg: String?) {

            }

            override fun finish(success: Boolean) {

            }

        })
複製程式碼

得到的輸出結果為:

zip.png
使用 zip 合併時,會等待每一次的合併項都傳送完畢後再傳送下一輪的事件。當我們需要從兩個資料來源拿資料,但是需要統一合併顯示時可以使用 zip 操作符對事件流進行合併。

多個 observable 的合併: zip() 的多事件合併就有點厲害了,支援九個 Observable 按資料型別合併,除了兩個觀察者物件合併時 zipper 是 BiFunction 其他的為 FunctionX ,X 為合併個數。如果多於九個觀察者物件合併,與上面兩種合併一樣可以使用 zipArray() 進行合併,但是合併後的觀察結果是一個 Object 陣列物件,需要自己判斷資料型別

結語

除了以上的建立 Observable 物件級別的合併操作符,還有一些事件流中的操作符,譬如第一段程式碼中的 concatMap ,在事件流前面新增其他事件的 startWith() 等。還是比較完善的

相關文章