在Eclipse下测试Rxjava中的操作符interval()时出现了很奇怪的问题,怎么试都不能执行。
代码如下:
Observable.interval(1, TimeUnit.SECONDS)
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
// TODO Auto-generated method stub
System.out.println("onCompleted ");
}
@Override
public void onError(Throwable arg0) {
// TODO Auto-generated method stub
}
@Override
public void onNext(Long arg0) {
// TODO Auto-generated method stub
System.out.println("onNext " + arg0);
}
});
这段代码在Eclipse下面运行没有达到我们预期的每隔一秒输出一个数字的结果。
怎么解决呢?
去官网找了下原因,也找到了解决方案:
https://github.com/ReactiveX/RxJava/issues/3932
Observable.interval(1, TimeUnit.SECONDS)
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
// TODO Auto-generated method stub
System.out.println("onCompleted ");
}
@Override
public void onError(Throwable arg0) {
// TODO Auto-generated method stub
}
@Override
public void onNext(Long arg0) {
// TODO Auto-generated method stub
System.out.println("onNext " + arg0);
}
});
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
这样就能输出结果了如下所示:
onNext 0
onNext 1
onNext 2
onNext 3
onNext 4
onNext 5
onNext 6
......
让程序沉睡一段时间似乎也不是很完美的解决方案,继续找就找到了如下的解决办法(https://github.com/ReactiveX/RxJava/issues/4132)。
具体原因如下:
When you use the default scheduler (Schedulers.computation()) the observable emits on another thread. If your program exits just after the subscribe then the observable is not given a chance to run. Put in a long sleep just after the subscribe() call and you will see it working.
最终代码如下:
Observable.interval(1, TimeUnit.SECONDS, Schedulers.trampoline())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
// TODO Auto-generated method stub
System.out.println("onCompleted ");
}
@Override
public void onError(Throwable arg0) {
// TODO Auto-generated method stub
}
@Override
public void onNext(Long arg0) {
// TODO Auto-generated method stub
System.out.println("onNext " + arg0);
}
});
需要注意的是,上面代码中的 Schedulers.trampoline()替换为Schedulers.immediate()也是可以运行的,但是这样做是不安全的。
The immediate() scheduler is not safe for recursive scheduling on the current thread, use trampoline() instead.
首先我们看看源码:
/**
* Creates and returns a {@link Scheduler} that executes work immediately on the current thread.
*
* @return a {@link Scheduler} that executes work immediately
*/
public static Scheduler immediate() {
return rx.internal.schedulers.ImmediateScheduler.INSTANCE;
}
/**
* Creates and returns a {@link Scheduler} that queues work on the current thread to be executed after the
* current work completes.
*
* @return a {@link Scheduler} that queues work on the current thread
*/
public static Scheduler trampoline() {
return rx.internal.schedulers.TrampolineScheduler.INSTANCE;
}
从上面源码可以看出,immediate()方法返回的是ImmediateScheduler类的实例,而trampoline()方法返回的是TrampolineScheduler类的实例。
ImmediateScheduler类的部分源码:
/**
* Executes work immediately on the current thread.
*/
public final class ImmediateScheduler extends Scheduler {
public static final ImmediateScheduler INSTANCE = new ImmediateScheduler();
private ImmediateScheduler() {
// the class is singleton
}
从ImmediateScheduler类的官网介绍中可以看出ImmediateScheduler执行在当前main线程。
TrampolineScheduler类的部分源码:
/**
* Schedules work on the current thread but does not execute immediately. Work is put in a queue and executed
* after the current unit of work is completed.
*/
public final class TrampolineScheduler extends Scheduler {
public static final TrampolineScheduler INSTANCE = new TrampolineScheduler();
@Override
public Worker createWorker() {
return new InnerCurrentThreadScheduler();
}
从TrampolineScheduler类的官网介绍中可以看出TrampolineScheduler不会立即执行,当其他排队任务介绍时才执行,当然TrampolineScheduler运行在当前main线程。
下表展示了RxJava中可用的调度器种类:
调度器类型 | 效果 |
---|---|
Schedulers.computation( ) | 用于计算任务,如事件循环或和回调处理,不要用于IO操作(IO操作请使用Schedulers.io());默认线程数等于处理器的数量 |
Schedulers.from(executor) | 使用指定的Executor作为调度器 |
Schedulers.immediate( ) | 在当前线程立即开始执行任务 |
Schedulers.io( ) | 用于IO密集型任务,如异步阻塞IO操作,这个调度器的线程池会根据需要增长;对于普通的计算任务,请使用Schedulers.computation();Schedulers.io( )默认是一个CachedThreadScheduler,很像一个有线程缓存的新线程调度器 |
Schedulers.newThread( ) | 为每个任务创建一个新线程 |
Schedulers.trampoline( ) | 当其它排队的任务完成后,在当前线程排队开始执行 |
在RxJava中,某些Observable操作符的变体允许你设置用于操作执行的调度器,其它的则不在任何特定的调度器上执行,或者在一个指定的默认调度器上执行。下面的表格个列出了一些操作符的默认调度器:
操作符 | 调度器 |
---|---|
buffer(timespan) | computation |
buffer(timespan, count) | computation |
buffer(timespan, timeshift) | computation |
debounce(timeout, unit) | computation |
delay(delay, unit) | computation |
delaySubscription(delay, unit) | computation |
interval | computation |
repeat | trampoline |
replay(time, unit) | computation |
replay(buffersize, time, unit) | computation |
replay(selector, time, unit) | computation |
replay(selector, buffersize, time, unit) | computation |
retry | trampoline |
sample(period, unit) | computation |
skip(time, unit) | computation |
skipLast(time, unit) | computation |
take(time, unit) | computation |
takeLast(time, unit) | computation |
takeLast(count, time, unit) | computation |
takeLastBuffer(time, unit) | computation |
takeLastBuffer(count, time, unit) | computation |
throttleFirst | computation |
throttleLast | computation |
throttleWithTimeout | computation |
timeInterval | immediate |
timeout(timeoutSelector) | immediate |
timeout(firstTimeoutSelector, timeoutSelector) | immediate |
timeout(timeoutSelector, other) | immediate |
timeout(timeout, timeUnit) | computation |
timeout(firstTimeoutSelector, timeoutSelector, other) | immediate |
timeout(timeout, timeUnit, other) | computation |
timer | computation |
timestamp | immediate |
window(timespan) | computation |
window(timespan, count) | computation |
window(timespan, timeshift) | computation |
了解更多Scheduler使用请参考:
https://github.com/mcxiaoke/RxDocs/blob/master/Scheduler.md