RxJava中遇到的坑
1.線程無限創建
在輪詢的請求中使用.subscribeOn(Schedulers.io())可能會導致無限創建線程問題。
Schedulers是RxJava的主要組件之一。負責在不同線程上執行Observable的操作,便於將耗時任務分攤到其他線程。
我們這裏需要連了解下Schedulers下的不同的Scheduler的屬性和使用:
IOScheduler
最常見的調度器之一。用於IO相關操作。比如網絡請求和文件操作。IO 調度器背後由線程池支撐。
它首先創建一個工作線程,可以複用於其他操作。當然,當這個工作線程(長時間任務的情況)不能被複用時,會創建一個新的線程來處理其他操作。這個好處也會帶來一些問題,因爲它允許創建的線程是無限的,所以當創建大量線程的時候,會對整體性能造成一些影響。
subscribeOn(Schedulers.io())
ComputationScheduler
作爲計算密集型的 Scheduler,Schedulers.computation( )用於計算任務,如事件循環或和回調處理,不要用於IO操作(IO操作請使用Schedulers.io());ComputationScheduler的線程數是與 CPU 核心密切相關的,原因是當線程數遠遠超過 CPU 核心數目時,CPU 的時間更多的損耗在了線程的上下文切換,默認線程數等於處理器的數量
subscribeOn(Schedulers.computation())
Single
由一個線程支持。所以無論有多少個Observable,都將只運行在這個線程上。
subscribeOn(Schedulers.single())
Immediate
此調度器在當前活躍線程以阻塞的方式開始其任務(rxjava2已將其移除),無視當前運行的任務。使用RxJava2的可以忽略。
subscribeOn(Schedulers.immediate())
Trampoline
此調度器運行在當前線程,所以如果你有代碼運行在主線程上,它會將將要運行的代碼塊添加到主線程的隊列。和Immediate非常相似,不同的是Immediate會阻塞此線程,而Trampoline會等待當前任務執行完成。適用用於不止一個Observable,並且希望它們能夠按照順序執行的場景。
subscribeOn(Schedulers.trampoline())
Observable.just(1,2,3,4)
.subscribeOn(Schedulers.trampoline())
.subscribe(onNext);
Observable.just( 5,6, 7,8, 9)
.subscribeOn(Schedulers.trampoline())
.subscribe(onNext);
Output:
Number = 1
Number = 2
Number = 3
Number = 4
Number = 5
Number = 6
Number = 7
Number = 8
Number = 9
Executor Scheduler()
更像是一種自定義的IO調度器。我們可以通過制定線程池的大小來創建一個自定義的線程池。適用於Observable的數量對於IO調度器太多的場景使用,使用如下:
Scheduler ioOne = Schedulers.from(Executors.newFixedThreadPool(5));
Scheduler fixScheduler = Schedulers.from(ioOne)
subscribeOn(fixScheduler)
Android Scheduler
由rxAndroid庫提供,用於將操作切換到主線程以便操作ui,經常用於observeOn方法。使用如下:
AndroidSchedulers.mainThread()
observeOn(AndroidSchedulers.mainThread())
之所以在輪詢請求情況下回出現無限創建線程和IOScheduler的實現密切相關,當任務一直在執行或者來不及釋放之前的線程就會創建新的線程。所以很極限的情況就是輪訓時間過短到之前一個的線程還沒有完成任務釋放出來,就會先創建一個線程出來,這個時候我們應該使用Executor Scheduler調度器自定義線程池的個數。
Scheduler ioOne = Schedulers.from(Executors.newFixedThreadPool(5));
Scheduler fixScheduler = Schedulers.from(ioOne)
2.Interval和操作符一起使用操作符中操作發生異常會中斷整個Interval
Observable.interval(tripInterval, TimeUnit.SECONDS)
.flatMap((Function<Long, ObservableSource<Response<TripResult>>>) aLong ->
AppDelegateImpl.getInstance()
.getInfoService()
.getTripInfo())
.subscribeOn(SchedulerPoolHelper.getInstance().getIoTrip())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new NetConsumer<TripResult>() {
@Override
public void onSuccess(TripResult result) {
processTripState(result);
}
}, throwable -> {
LogUtils.d("Trip: " + throwable.getMessage());
});
比如說:我每隔tripInterval秒回去請求一次接口,我們把請求接口放到中間的flatMap操作符中,一旦因爲請求超時或者是一次髒數據導致請求失敗,則整個interval都會中斷。
這個問題導致我們的請求可能由於網絡較差失敗過一次後,就不在有請求了。
所以這種情況我是把請求嵌套在了Consumer的onSuccess中了,這樣就不會中斷了。
Observable.interval(tripInterval, TimeUnit.SECONDS)
.subscribeOn(SchedulerPoolHelper.getInstance().getIoTrip())
.subscribe(aLong -> {
AppDelegateImpl.getInstance().getInfoService()
.getTripInfo()
.subscribeOn(SchedulerPoolHelper.getInstance().getIoTrip())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new NetConsumer<TripResult>() {
@Override
public void onSuccess(TripResult result) {
processTripState(result);
}
}, throwable -> LogUtils.d(TAG, "Trip: " + throwable.getMessage()));
});
查閱了一些資料沒有發現好的解決方案,如果大佬們有不同的簡潔可以在留言區留言提出不同的方案。