RxJava 三:RxJava 3 線程調度

目錄

1.Scheduler 類型

2.Scheduler 線程控制接口:

3.Schedules線程自由切換

4.延伸:利用subscribeOn() 指定doOnSubscribe()執行線程


RxJava 默認事件的產生和消費都是在同一個線程的。也就是說,默認實現的只是一個同步的觀察者模式,即:在哪個線程調用 subscribe()(訂閱),就在哪個線程生產事件;在哪個線程生產事件,就在哪個線程消費事件,此時數據發送和數據接收是一一對應的,即發送一個,接收一個,再發送一個,再接收一個……。如果需要切換線程,就需要用到 Scheduler (調度器),它可以讓RxJava的線程切換變得簡單明瞭,輕鬆實現異步。

1.Scheduler 類型

Scheduler:線程控制器 / 調度器,RxJava 通過它來指定每一段代碼應該運行在什麼樣的線程。RxJava內置幾種 Scheduler 類型,基本適合大多數的使用場景:

(1)Schedulers.newThread(): 總是啓用新線程,並在新線程執行操作。

(2)Schedulers.io():

用於IO密集型的操作,例如讀寫SD卡文件,查詢數據庫,網絡信息交互等,具有線程緩存機制。行爲模式和 newThread() 差不多,區別在於 io() 的內部實現是是用一個無數量上限的線程池,可以重用空閒的線程。調度器接收到任務後,先檢查線程緩存池中,如果有空閒的線程,則複用,否則創建新的線程,並加入到線程池中,如果每次都沒有空閒線程使用,可以無上限的創建新線程。因此多數情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中,可以避免創建不必要的線程。

(3)Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算,即不會被 I/O 等操作限制性能的操作,例如圖形的計算。這個 Scheduler 使用的固定的線程池,大小爲 CPU 核數。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU。

(4)Schedulers.trampoline()當前線程立即執行任務,如果當前線程有任務在執行,則會將其暫停,等插入進來的任務執行完之後,再將未完成的任務接着執行。

(5)Schedulers.single():擁有一個線程單例,所有的任務都在這一個線程中執行,當此線程中有任務執行時,其他任務將會按照先進先出的順序依次執行。

(6)Scheduler.from(@NonNull Executor executor):指定一個線程調度器,由此調度器來控制任務的執行策略。

(7)另外,Android 還有一個專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行。

2.Scheduler 線程控制接口:

subscribeOn(): 指定 subscribe() 所發生的線程,即事件產生的線程。若多次設定,則只有一次起作用。

observeOn():指定 Observer 所運行在的線程,事件消費的線程。若多次設定,每次均起作用。

看下面例子:

Observable.just(1, 2, 3, 4).subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())

        .subscribe(new Consumer<Integer>() {

            @Override

            public void accept(Integer arg0) throws Exception {

                // TODO Auto-generated method stub

            }

        });

subscribeOn(Schedulers.io()) 指定事件產生 1、2、3、4 將會在 IO 線程發出;而由於observeOn(AndroidScheculers.mainThread()) 的指定,因此訂閱者數字的打印將發生在主線程 。事實上,這種在 subscribe() 之前寫上兩句 subscribeOn(Scheduler.io()) 和 observeOn(AndroidSchedulers.mainThread()) 的使用方式非常常見,它適用於多數的 『後臺線程取數據,主線程顯示』的程序策略。這就意味着,即使後臺IO耗費了幾十甚至幾百毫秒的時間,也不會造成絲毫界面的卡頓。

3.Schedules線程自由切換

結合map() flatMap() 等變換方法,Scheduler 可以實現線程的自由切換。因爲 observeOn() 指定的是 Observer的線程,而這個 Observer並不是(嚴格說應該爲不一定是)subscribe() 參數中的 Observer,而是 observeOn() 執行時的當前 Observable 所對應的 Observer,即它的直接下級 Observer。換句話說,observeOn() 指定的是它之後的操作所在的線程。因此如果有多次切換線程的需求,只要在每個想要切換線程的位置調用一次 observeOn() 即可。

Observable.just(1, 2, 3, 4) // IO 線程,由 subscribeOn() 指定
        .subscribeOn(Schedulers.io())
        .observeOn(Schedulers.newThread())
        .map(mapOperator) // 新線程,由 observeOn() 指定
        .observeOn(Schedulers.io())
        .map(mapOperator2) // IO 線程,由 observeOn() 指定
        .observeOn(AndroidSchedulers.mainThread)
        .subscribe(observer);  // Android 主線程,由 observeOn() 指定

通過 observeOn() 的多次調用,程序實現了線程的多次切換。不過,不同於 observeOn() , subscribeOn() 的位置放在哪裏都可以,但它是隻能調用一次的。

4.延伸:利用subscribeOn() 指定doOnSubscribe()執行線程

雖然當使用了多個subscribeOn() 切換線程的時候,只有第一個 subscribeOn() 起作用。對事件處理的流程(線程切換)沒有影響,但在流程之前卻是可以利用的。

Observer(這裏指的是RxJava 3裏的幾個Observer的實現類,如ResourceObserver) 的 onStart() 可以用作流程開始前的初始化。然而 onStart() 由於在 subscribe() 發生時就被調用了,因此不能指定線程,只能執行在 subscribe() 被調用時的線程。這就導致如果 onStart() 中含有對線程有要求的代碼(例如在界面上顯示一個 ProgressBar,這必須在主線程執行),將會有線程非法的風險,因爲有時你無法預測 subscribe() 將會在什麼線程執行。

而與 ResourceObserver.onStart() 相對應的,有一個方法 Observable.doOnSubscribe() 。它和 ResourceObserver.onStart() 同樣是在 subscribe()調用後而且在事件發送前執行,但區別在於它可以指定線程。默認情況下, doOnSubscribe() 執行在 subscribe() 發生的線程;而如果在 doOnSubscribe() 之後有 subscribeOn() 的話,它將執行在離它最近的 subscribeOn() 所指定的線程。也就是在 doOnSubscribe()的後面跟一個 subscribeOn() ,就能指定準備工作的線程了。

Observable.just(1,2,3,4)
        .subscribeOn(Schedulers.io())
        .doOnSubscribe(new Consumer<Disposable>() {
            @Override
            public void accept(Disposable disposable) throws Exception {
                progressBar.setVisibility(View.VISIBLE); // 需要在主線程執行
            }
        })
        .subscribeOn(AndroidSchedulers.mainThread()) // 指定主線程
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new ResourceObserver<Integer>() {
            @Override
            protected void onStart() {
                super.onStart();
            }
            @Override
            public void onNext(Integer integer) { }
            @Override
            public void onError(Throwable e) { }
            @Override
            public void onComplete() { }
        });

 

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