給 Android 開發者的 RxJava 詳解


RxJava 到底是什麼
一個詞:異步。
RxJava 在 GitHub 主頁上的自我介紹是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個在 Java VM 上使用可觀測的序列來組成異步的、基於事件的程序的庫)。這就是 RxJava ,概括得非常精準。
RxJava 好在哪
一個詞:簡潔。
 Android 創造的AsyncTask 和Handler ,其實都是爲了讓異步代碼更加簡潔。RxJava 的優勢也是簡潔,但它的簡潔的與衆不同之處在於,隨着程序邏輯變得越來越複雜,它依然能夠保持簡潔。
RxJava 的異步實現,是通過一種擴展的觀察者模式來實現的。
而 RxJava 作爲一個工具庫,使用的就是通用形式的觀察者模式。
RxJava 的觀察者模式
RxJava 有四個基本概念:Observable (可觀察者,即被觀察者)、 Observer (觀察者)、 subscribe (訂閱)、事件。Observable 和Observer 通過 subscribe() 方法實現訂閱關係,從而 Observable 可以在需要的時候發出事件來通知 Observer。
與傳統觀察者模式不同, RxJava 的事件回調方法除了普通事件 onNext() (相當於 onClick() / onEvent())之外,還定義了兩個特殊的事件:onCompleted() 和 onError()。
基於以上的概念, RxJava 的基本實現主要有三點:
1) 創建 Observer
Observer 即觀察者,它決定事件觸發的時候將有怎樣的行爲。 
除了 Observer 接口之外,RxJava 還內置了一個實現了 Observer 的抽象類:Subscriber。 Subscriber 對 Observer 接口進行了一些擴展,但他們的基本使用方式是完全一樣的:

所以如果你只想使用基本功能,選擇 Observer 和 Subscriber 是完全一樣的。它們的區別對於使用者來說主要有兩點:
  1. onStart(): 這是 Subscriber 增加的方法。它會在 subscribe 剛開始,而事件還未發送之前被調用,可以用於做一些準備工作,例如數據的清零或重置。這是一個可選方法,默認情況下它的實現爲空。需要注意的是,如果對準備工作的線程有要求(例如彈出一個顯示進度的對話框,這必須在主線程執行), onStart() 就不適用了,因爲它總是在 subscribe 所發生的線程被調用,而不能指定線程。要在指定的線程來做準備工作,可以使用 doOnSubscribe() 方法,具體可以在後面的文中看到。
  2. unsubscribe(): 這是 Subscriber 所實現的另一個接口 Subscription 的方法,用於取消訂閱。在這個方法被調用後,Subscriber 將不再接收事件。一般在這個方法調用前,可以使用 isUnsubscribed() 先判斷一下狀態。 unsubscribe() 這個方法很重要,因爲在 subscribe() 之後, Observable 會持有 Subscriber 的引用,這個引用如果不能及時被釋放,將有內存泄露的風險。所以最好保持一個原則:要在不再使用的時候儘快在合適的地方(例如 onPause() onStop() 等方法中)調用unsubscribe() 來解除引用關係,以避免內存泄露的發生。
2) 創建 Observable
Observable 即被觀察者,它決定什麼時候觸發事件以及觸發怎樣的事件。
當 Observable 被訂閱的時候,觀察者Subscriber 將會被調用三次 onNext() 和一次 onCompleted())。這樣,由被觀察者調用了觀察者的回調方法,就實現了由被觀察者向觀察者的事件傳遞,即觀察者模式。
create() 方法是 RxJava 最基本的創造事件序列的方法。基於這個方法, RxJava 還提供了一些方法用來快捷創建事件隊列,例如:
  • just(T...): 將傳入的參數依次發送出來。
  • from(T[]) / from(Iterable<? extends T>) : 將傳入的數組或 Iterable 拆分成具體對象後,依次發送出來。

3) Subscribe (訂閱)
創建了 Observable 和 Observer 之後,再用 subscribe() 方法將它們聯結起來,整條鏈子就可以工作了。代碼形式很簡單:
對流式 API 的設計會造成造成影響
可以看到,subscriber() 做了3件事:
  1. 調用 Subscriber.onStart() 。這個方法在前面已經介紹過,是一個可選的準備方法。
  2. 調用 Observable 中的 OnSubscribe.call(Subscriber) 。在這裏,事件發送的邏輯開始運行。從這也可以看出,在 RxJava 中,Observable 並不是在創建的時候就立即開始發送事件,而是在它被訂閱的時候,即當 subscribe() 方法執行的時候。
  3. 將傳入的 Subscriber 作爲 Subscription 返回。這是爲了方便 unsubscribe().

在 RxJava 的默認規則中,事件的發出和消費都是在同一個線程的。也就是說,如果只用上面的方法,實現出來的只是一個同步的觀察者模式。觀察者模式本身的目的就是『後臺處理,前臺回調』的異步機制,因此異步對於 RxJava 是至關重要的。而要實現異步,則需要用到 RxJava 的另一個概念: Scheduler 。
在RxJava 中,Scheduler ——調度器,相當於線程控制器,RxJava 通過它來指定每一段代碼應該運行在什麼樣的線程。RxJava 已經內置了幾個 Scheduler ,它們已經適合大多數的使用場景:
  • Schedulers.immediate(): 直接在當前線程運行,相當於不指定線程。這是默認的 Scheduler
  • Schedulers.newThread(): 總是啓用新線程,並在新線程執行操作。
  • Schedulers.io(): I/O 操作(讀寫文件、讀寫數據庫、網絡信息交互等)所使用的 Scheduler。行爲模式和 newThread() 差不多,區別在於 io() 的內部實現是是用一個無數量上限的線程池,可以重用空閒的線程,因此多數情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中,可以避免創建不必要的線程。
  • Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算,即不會被 I/O 等操作限制性能的操作,例如圖形的計算。這個 Scheduler 使用的固定的線程池,大小爲 CPU 核數。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU。
  • 另外, Android 還有一個專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行。
有了這幾個 Scheduler ,就可以使用 subscribeOn() 和 observeOn() 兩個方法來對線程進行控制了。 * subscribeOn(): 指定subscribe() 所發生的線程,即 Observable.OnSubscribe 被激活時所處的線程。或者叫做事件產生的線程。 * observeOn(): 指定Subscriber 所運行在的線程。或者叫做事件消費的線程。
RxJava 提供了對事件序列進行變換的支持,這是它的核心功能之一,也是大多數人說『RxJava 真是太好用了』的最大原因。所謂變換,就是將事件序列中的對象或整個序列進行加工處理,轉換成不同的事件或事件序列。

FuncX 和ActionX 的區別在 FuncX 包裝的是有返回值的方法。
map(): 事件對象的直接變換,具體功能上面已經介紹過。它是 RxJava 最常用的變換。
flatMap(): 需要注意,和 map() 不同的是, flatMap() 中返回的是個 Observable 對象,並且這個 Observable 對象並不是被直接發送到了 Subscriber 的回調方法中。flatMap() 的原理是這樣的:1. 使用傳入的事件對象創建一個 Observable 對象;2. 並不發送這個 Observable, 而是將它激活,於是它開始發送事件;3. 每一個創建出來的 Observable 發送的事件,都被匯入同一個 Observable ,而這個 Observable 負責將這些事件統一交給Subscriber 的回調方法。這三個步驟,把事件拆成了兩級,通過一組新創建的 Observable 將初始的對象『鋪平』之後通過統一路徑分發了下去。
  • throttleFirst(): 在每次事件觸發後的一定時間間隔內丟棄新的事件。常用作去抖動過濾,例如按鈕的點擊監聽器:RxView.clickEvents(button) // RxBinding 代碼,後面的文章有解釋 .throttleFirst(500, TimeUnit.MILLISECONDS) // 設置防抖間隔爲 500ms .subscribe(subscriber);媽媽再也不怕我的用戶手抖點開兩個重複的界面啦。

在 RxJava 的內部,它們是基於同一個基礎的變換方法:lift(Operator)
compose: 對 Observable 整體的變換
除了 lift() 之外, Observable 還有一個變換方法叫做 compose(Transformer)。它和 lift() 的區別在於, lift() 是針對事件項和事件序列的,而 compose() 是針對 Observable 自身進行變換。

除了靈活的變換,RxJava 另一個牛逼的地方,就是線程的自由控制。
通過 observeOn() 的多次調用,程序實現了線程的多次切換。
不過,不同於 observeOn() , subscribeOn() 的位置放在哪裏都可以,但它是隻能調用一次的。
與 Subscriber.onStart() 相對應的,有一個方法 Observable.doOnSubscribe() 。它和 Subscriber.onStart() 同樣是在 subscribe()調用後而且在事件發送前執行,但區別在於它可以指定線程。默認情況下, doOnSubscribe() 執行在 subscribe() 發生的線程;而如果在 doOnSubscribe() 之後有 subscribeOn() 的話,它將執行在離它最近的 subscribeOn() 所指定的線程。

RxJava的基本概念
是跟觀察者模式不同的地方就在於,如果沒有觀察者(即Subscribers),Observables是不會發出任何事件的
操作符大致分爲以下幾種:
  • Creating Observables(Observable的創建操作符),比如:Observable.create()、Observable.just()、Observable.from()等等;
  • Transforming Observables(Observable的轉換操作符),比如:observable.map()、observable.flatMap()、observable.buffer()等等;
  • Filtering Observables(Observable的過濾操作符),比如:observable.filter()、observable.sample()、observable.take()等等;
  • Combining Observables(Observable的組合操作符),比如:observable.join()、observable.merge()、observable.combineLatest()等等;
  • Error Handling Operators(Observable的錯誤處理操作符),比如:observable.onErrorResumeNext()、observable.retry()等等;
  • Observable Utility Operators(Observable的功能性操作符),比如:observable.subscribeOn()、observable.observeOn()、observable.delay()等等;
  • Conditional and Boolean Operators(Observable的條件操作符),比如: observable.amb()、observable.contains()、observable.skipUntil()等等;
  • Mathematical and Aggregate Operators(Observable數學運算及聚合操作符),比如:observable.count()、observable.reduce()、observable.concat()等等;
  • 其他如observable.toList()、observable.connect()、observable.publish()等等;

創建型操作符

defer操作符和just操作符的區別:just操作符是在創建Observable就進行了賦值操作,而defer是在訂閱者訂閱時才創建Observable,此時才進行真正的賦值操作

timer操作符是創建一串連續的數字,產生這些數字的時間間隔是一定的;這裏有兩種情況:
一種是隔一段時間產生一個數字,然後就結束,可以理解爲延遲產生數字
一種是每隔一段時間就產生一個數字,沒有結束符,也就是是可以產生無限個連續的數字

interval操作符是每隔一段時間就產生一個數字,這些數字從0開始,一次遞增1直至無窮大;interval操作符的實現效果跟上面的timer操作符的第二種情形一樣。

range操作符是創建一組在從n開始,個數爲m的連續數字,比如range(3,10),就是創建3、4、5…12的一組數字

repeat操作符是對某一個Observable,重複產生多次結果

repeatWhen操作符是對某一個Observable,有條件地重新訂閱從而產生多次結果

Transforming Observables(Observable的轉換操作符)

buffer操作符週期性地收集源Observable產生的結果到列表中,並把這個列表提交給訂閱者,訂閱者處理後,清空buffer列表,同時接收下一次收集的結果並提交給訂閱者,周而復始。
需要注意的是,一旦源Observable在產生結果的過程中出現異常,即使buffer已經存在收集到的結果,訂閱者也會馬上收到這個異常,並結束整個過程。

flatMap操作符是把Observable產生的結果轉換成多個Observable,然後把這多個Observable“扁平化”成一個Observable,並依次提交產生的結果給訂閱者。
flatMap操作符通過傳入一個函數作爲參數轉換源Observable,在這個函數中,你可以自定義轉換規則,最後在這個函數中返回一個新的Observable,然後flatMap操作符通過合併這些Observable結果成一個Observable,並依次提交結果給訂閱者。

cancatMap操作符與flatMap操作符類似,都是把Observable產生的結果轉換成多個Observable,然後把這多個Observable“扁平化”成一個Observable,並依次提交產生的結果給訂閱者。
與flatMap操作符不同的是,concatMap操作符在處理產生的Observable時,採用的是“連接(concat)”的方式,而不是“合併(merge)”的方式,這就能保證產生結果的順序性,也就是說提交給訂閱者的結果是按照順序提交的,不會存在交叉的情況。

switchMap操作符與flatMap操作符類似,都是把Observable產生的結果轉換成多個Observable,然後把這多個Observable“扁平化”成一個Observable,並依次提交產生的結果給訂閱者。
與flatMap操作符不同的是,switchMap操作符會保存最新的Observable產生的結果而捨棄舊的結果,舉個例子來說,比如源Observable產生A、B、C三個結果,通過switchMap的自定義映射規則,映射後應該會產生A1、A2、B1、B2、C1、C2,但是在產生B2的同時,C1已經產生了,這樣最後的結果就變成A1、A2、B1、C1、C2,B2被捨棄掉了!

groupBy操作符是對源Observable產生的結果進行分組,形成一個類型爲GroupedObservable的結果集,GroupedObservable中存在一個方法爲getKey(),可以通過該方法獲取結果集的Key值(類似於HashMap的key)。
值得注意的是,由於結果集中的GroupedObservable是把分組結果緩存起來,如果對每一個GroupedObservable不進行處理(既不訂閱執行也不對其進行別的操作符運算),就有可能出現內存泄露。因此,如果你對某個GroupedObservable不進行處理,最好是對其使用操作符take(0)處理。

map操作符是把源Observable產生的結果,通過映射規則轉換成另一個結果集,並提交給訂閱者進行處理。

cast操作符類似於map操作符,不同的地方在於map操作符可以通過自定義規則,把一個值A1變成另一個值A2,A1和A2的類型可以一樣也可以不一樣;而cast操作符主要是做類型轉換的,傳入參數爲類型class,如果源Observable產生的結果不能轉成指定的class,則會拋出ClassCastException運行時異常。

scan操作符通過遍歷源Observable產生的結果,依次對每一個結果項按照指定規則進行運算,計算後的結果作爲下一個迭代項參數,每一次迭代項都會把計算結果輸出給訂閱者。

window操作符非常類似於buffer操作符,區別在於buffer操作符產生的結果是一個List緩存,而window操作符產生的結果是一個Observable,訂閱者可以對這個結果Observable重新進行訂閱處理。

Filtering Observables(Observable的過濾操作符)

debounce操作符對源Observable每產生一個結果後,如果在規定的間隔時間內沒有別的結果產生,則把這個結果提交給訂閱者處理,否則忽略該結果。
值得注意的是,如果源Observable產生的最後一個結果後在規定的時間間隔內調用了onCompleted,那麼通過debounce操作符也會把這個結果提交給訂閱者。

distinct操作符對源Observable產生的結果進行過濾,把重複的結果過濾掉,只輸出不重複的結果給訂閱者,非常類似於SQL裏的distinct關鍵字。

elementAt操作符在源Observable產生的結果中,僅僅把指定索引的結果提交給訂閱者,索引是從0開始的。其流程圖如下: 

filter操作符是對源Observable產生的結果按照指定條件進行過濾,只有滿足條件的結果纔會提交給訂閱者

ofType操作符類似於filter操作符,區別在於ofType操作符是按照類型對結果進行過濾

first操作符是把源Observable產生的結果的第一個提交給訂閱者,first操作符可以使用elementAt(0)和take(1)替代。

single操作符是對源Observable的結果進行判斷,如果產生的結果滿足指定條件的數量不爲1,則拋出異常,否則把滿足條件的結果提交給訂閱者

last操作符把源Observable產生的結果的最後一個提交給訂閱者,last操作符可以使用takeLast(1)替代。

ignoreElements操作符忽略所有源Observable產生的結果,只把Observable的onCompleted和onError事件通知給訂閱者。ignoreElements操作符適用於不太關心Observable產生的結果,只是在Observable結束時(onCompleted)或者出現錯誤時能夠收到通知。

sample操作符定期掃描源Observable產生的結果,在指定的時間間隔範圍內對源Observable產生的結果進行採樣

skip操作符針對源Observable產生的結果,跳過前面n個不進行處理,而把後面的結果提交給訂閱者處理

skipLast操作符針對源Observable產生的結果,忽略Observable最後產生的n個結果,而把前面產生的結果提交給訂閱者處理

take操作符是把源Observable產生的結果,提取前面的n個提交給訂閱者,而忽略後面的結果

takeFirst操作符類似於take操作符,同時也類似於first操作符,都是獲取源Observable產生的結果列表中符合指定條件的前一個或多個,與first操作符不同的是,first操作符如果獲取不到數據,則會拋出NoSuchElementException異常,而takeFirst則會返回一個空的Observable,該Observable只有onCompleted通知而沒有onNext通知。

takeLast操作符是把源Observable產生的結果的後n項提交給訂閱者,提交時機是Observable發佈onCompleted通知之時。

Combining Observables(Observable的組合操作符)

combineLatest操作符把兩個Observable產生的結果進行合併,合併的結果組成一個新的Observable。這兩個Observable中任意一個Observable產生的結果,都和另一個Observable最後產生的結果,按照一定的規則進行合併。

join操作符把類似於combineLatest操作符,也是兩個Observable產生的結果進行合併,合併的結果組成一個新的Observable,但是join操作符可以控制每個Observable產生結果的生命週期,在每個結果的生命週期內,可以與另一個Observable產生的結果按照一定的規則進行合併

groupJoin操作符非常類似於join操作符,區別在於join操作符中第四個參數的傳入函數不一致

merge操作符是按照兩個Observable提交結果的時間順序,對Observable進行合併,如ObservableA每隔500毫秒產生數據爲0,5,10,15,20;而ObservableB每隔500毫秒產生數據0,10,20,30,40,其中第一個數據延遲500毫秒產生,最後合併結果爲:0,0,5,10,10,20,15,30,20,40

從merge操作符的流程圖可以看出,一旦合併的某一個Observable中出現錯誤,就會馬上停止合併,並對訂閱者回調執行onError方法,而mergeDelayError操作符會把錯誤放到所有結果都合併完成之後才執行

startWith操作符是在源Observable提交結果之前,插入指定的某些數據

switchOnNext操作符是把一組Observable轉換成一個Observable,轉換規則爲:對於這組Observable中的每一個Observable所產生的結果,如果在同一個時間內存在兩個或多個Observable提交的結果,只取最後一個Observable提交的結果給訂閱者

zip操作符是把兩個observable提交的結果,嚴格按照順序進行合併

Error Handling Operators(Observable的錯誤處理操作符)

onErrorReturn操作符是在Observable發生錯誤或異常的時候(即將回調oError方法時),攔截錯誤並執行指定的邏輯,返回一個跟源Observable相同類型的結果,最後回調訂閱者的onComplete方法

onErrorResumeNext操作符跟onErrorReturn類似,只不過onErrorReturn只能在錯誤或異常發生時只返回一個和源Observable相同類型的結果,而onErrorResumeNext操作符是在錯誤或異常發生時返回一個Observable,也就是說可以返回多個和源Observable相同類型的結果

onExceptionResumeNext操作符和onErrorResumeNext操作符類似,不同的地方在於onErrorResumeNext操作符是當Observable發生錯誤或異常時觸發,而onExceptionResumeNext是當Observable發生異常時才觸發。

retry操作符是當Observable發生錯誤或者異常時,重新嘗試執行Observable的邏輯,如果經過n次重新嘗試執行後仍然出現錯誤或者異常,則最後回調執行onError方法;當然如果源Observable沒有錯誤或者異常出現,則按照正常流程執行。

retryWhen操作符類似於retry操作符,都是在源observable出現錯誤或者異常時,重新嘗試執行源observable的邏輯,不同在於retryWhen操作符是在源Observable出現錯誤或者異常時,通過回調第二個Observable來判斷是否重新嘗試執行源Observable的邏輯,如果第二個Observable沒有錯誤或者異常出現,則就會重新嘗試執行源Observable的邏輯,否則就會直接回調執行訂閱者的onError方法。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章