TestScheduler 是专门用于测试的调度器,跟其他调度器的区别是TestScheduler只有被调用了时间才会继续。TestScheduler是一种特殊的、非线程安全的调度器,用于测试一些不引入真实并发性、允许手动推进虚拟时间的调度器。
在 RxJava2.x 中,原先RxJava1.x的Schedulers.test()被去掉了,想要获得TestScheduler对象可以通过直接new TestScheduler()的方式来实现。
TestScheduler 所包含的方法并不多,下面罗列几个关键的方法。
advanceTimeTo
将调度器的时钟移动到某个特定时刻。
例如:
时钟移动到10毫秒。
scheduler.advanceTimeTo(10, TimeUnit.MILLISECONDS);
时钟移动到20毫秒。
scheduler.advanceTimeBy(20, TimeUnit.MILLISECONDS);
下面的列子展示了0秒、20秒、40秒会打印不同的结果。
TestScheduler scheduler = new TestScheduler();
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("immediate");
}
});
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("20s");
}
},20, TimeUnit.SECONDS);
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("40s");
}
},40, TimeUnit.SECONDS);
scheduler.advanceTimeTo(1, TimeUnit.MILLISECONDS);
System.out.println("virtual time: " + scheduler.now(TimeUnit.MILLISECONDS));
scheduler.advanceTimeTo(20, TimeUnit.SECONDS);
System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));
scheduler.advanceTimeTo(40, TimeUnit.SECONDS);
System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));
执行结果:
immediate
virtual time: 1
20s
virtual time: 20
40s
virtual time: 40
我们可以看到使用advanceTimeTo之后,移动不同的时间点会打印不同的内容。
当然,advanceTimeTo()也可以传负数,表示回到过去的时间点。但是一般不推荐这种用法。
advanceTimeBy
将调度程序的时钟按指定的时间向前移动。
例如:
时钟移动了10毫秒。
scheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS);
再次调用刚才的方法,时钟又会移动了10毫秒。此时,时钟移动也到20毫秒,这是一个累加的过程。
scheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS);
下面的例子使用了timer操作符,timer是按照指定时间延迟发送的操作符,timer()并不会按周期地执行。该例子展示了2秒后atomicLong会自动加1
TestScheduler scheduler = new TestScheduler();
final AtomicLong atomicLong = new AtomicLong();
Observable.timer(2, TimeUnit.SECONDS, scheduler).subscribe(new Consumer<Long>() {
@Override
public void accept(final Long value) throws Exception {
atomicLong.incrementAndGet();
}
});
System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));
执行结果:
atomicLong's value=0, virtual time:0
atomicLong's value=0, virtual time:1
atomicLong's value=1, virtual time:2
这个结果符合预期,最初atomicLong为0,时钟移动到1秒它的值仍然为0,时钟再移动1秒相当于时钟移动到2秒,所以它的值变为1。
当然,advanceTimeBy()也可以传负数,表示回到过去。
TestScheduler scheduler = new TestScheduler();
final AtomicLong atomicLong = new AtomicLong();
Observable.timer(2, TimeUnit.SECONDS, scheduler).subscribe(new Consumer<Long>() {
@Override
public void accept(final Long value) throws Exception {
atomicLong.incrementAndGet();
}
});
System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));
scheduler.advanceTimeBy(-1, TimeUnit.SECONDS);
System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));
scheduler.advanceTimeBy(2, TimeUnit.SECONDS);
System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));
执行结果:
atomicLong's value=0, virtual time:0
atomicLong's value=0, virtual time:1
atomicLong's value=0, virtual time:0
atomicLong's value=1, virtual time:2
triggerActions
triggerActions不会修改时间,它执行任何计划中的但是未启动的任务,已经执行过的任务不会再启动。
TestScheduler scheduler = new TestScheduler();
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("immediate");
}
});
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("20s");
}
},20, TimeUnit.SECONDS);
System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));
执行结果:
virtual time: 0
稍微改一下代码,增加scheduler.triggerActions()
TestScheduler scheduler = new TestScheduler();
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("immediate");
}
});
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("20s");
}
},20, TimeUnit.SECONDS);
scheduler.triggerActions();
System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));
执行结果:
immediate
virtual time: 0
此时由于执行了triggerActions(),所以打印了immediate。
再改一下,增加advanceTimeBy()
TestScheduler scheduler = new TestScheduler();
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("immediate");
}
});
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("20s");
}
},20, TimeUnit.SECONDS);
scheduler.triggerActions();
System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));
scheduler.advanceTimeBy(20,TimeUnit.SECONDS);
执行结果:
immediate
virtual time: 0
20s
如果将triggerActions()放在最后,看看效果
TestScheduler scheduler = new TestScheduler();
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("immediate");
}
});
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
System.out.println("20s");
}
},20, TimeUnit.SECONDS);
System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));
scheduler.advanceTimeBy(20,TimeUnit.SECONDS);
scheduler.triggerActions();
执行结果:
virtual time: 0
immediate
20s
因为已经使用了advanceTimeBy(),所以即使再调用triggerActions()也不会执行已经启动过的任务。
总结
对于测试一些需要精确时间的任务,TestScheduler还是很有用处的,可以节省很多等待的时间。
参考资料: