RxJava2的Flowable observeOn線程調度順序問題

在項目的下載庫部分使用了PublishProcessor來實現支持有背壓的Observable,向外提供數據變化狀態,同時還對外提供了Single從數據庫裏面去獲取數據。在下載界面我們會首先去subscribe PublishProcessor以監聽數據變化,然後去拉取數據,填充到我們的界面裏。流程大致如下:

TD.downloader.observeTaskChange()
        .filter { it.change == TaskChange.TASK_ADD }
        .observeOn(AndroidSchedulers.mainThread())
        .`as`(RXUtils.autoDispose(lifecycleOwner))
        .subscribe { events ->
            ...
        }
...
TD.downloader.queryAll()
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe { tasks ->
            ...
        }

在下載庫裏,我們的query是先與這個PublishProcessor.onNext()運行的,但是在實際運行中我發現在少部分低端手機上面總是先執行了observeTaskChange裏面的代碼,然後才執行queryAll()裏面的代碼。通過打印日誌,我能很肯定代碼運行的流程沒問題,而如果我單步去調試,則總是先執行queryAll(),然後再執行observeTaskChange()。一時懷疑甚至懷疑這個Android系統的消息隊列有問題,但是想想如果是消息隊列序列有問題,那整個系統就完了。回頭想想那麼問題出在哪裏呢??既然問題出在主線程分發,那如果能夠在rxandroid打印一下postMessage的線程,就可以確定消息隊列的序列了。爲了驗證這個問題,我把rxandroid的項目下載了下來,然後把項目中的rxandroid庫換爲了本地的,然後在HandlerScheduler類的schedule方法裏sendMessageDelayed前加上了日誌,打印了線程ID。打印Log後,我驚呆了,居然PublishProcessor.onNext()在有Bug的情況下根本就不會觸發給Android的主線程發消息。於是我們可以猜測,應該是Flowable的其他地方已經把線程起起來了,然後通過Queue去把數據發送到了下游。通過Debug,然後發現FlowableObserveOn裏面的trySchedule會在Subscription request的時候去調度線程,而這個request是在subscribe()的時候就會調用,這裏就會去給主線程sendMessage了,如果手機配置不高,主線程執行速度慢,這個消息可能會有一定延遲纔會收到。而如果這個時候我們調用了PublishProcessor.onNext(),就不會去給主線程發送新的消息,因爲FlowableObserveOn裏面正在處理,所以直接排隊即可。這也就解釋了爲什麼在同一個線程裏面先返回了queryAll,後去PublishProcessor.onNext(),會先執行PublishProcessor的消費者代碼,後執行queryAll的消費者代碼。我這裏用Java寫了一個簡單的Demo驗證這個結論:

以下代碼的single()線程可以認爲是我上面說的Android主線程

public static void main(String[] args) {
    PublishProcessor<Integer> publishProcessor = PublishProcessor.create();
    Observable<Integer> observable = Observable.fromCallable(() -> 1)
            .observeOn(Schedulers.single());
    Flowable flowable = publishProcessor.hide().observeOn(Schedulers.single());
    Completable.fromCallable((Callable<Integer>) () -> {
        Thread.sleep(1000);
        return 1;
    }).subscribeOn(Schedulers.single())
            .subscribe();
 
    flowable.subscribe(res -> System.out.println(res));
    observable.subscribe(res -> System.out.println(res));
    publishProcessor.onNext(2);
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

輸出結果是:

2

1

證明了以上結論。

如何解決這個問題

這個問題的核心在於:
核心問題是熱Flowable在subscribe的時候就會觸發一次線程切換調度,不管有沒有發射數據。我們儘量是理清楚是否真的需要處理背壓,選擇合適的操作符,我這裏其實是不需要處理背壓的,所以直接加一句toObservable()即可完美解決這個問題。此外我們注意Flowable的subscribe和其他操作符的subscribe調用順序,也能完美解決這個問題。

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