接着上一篇的分享模式,今天我们介绍和分析线程切换操作符subscribeOn以及其源码设计。
Rxjava的一个最大优点之一就是灵活的线程切换,切换过程不影响整体链式逻辑流程,既方便又清新。为了对比,还是再次将一个操作符的结构图放上:
subscribeOn操作符用于切换事件源的线程,一般用在第一个observable的后面:
Observable.create((ObservableOnSubscribe<String>) e -> e.onNext("hello"))
.subscribeOn(Schedulers.newThread())
.subscribe(s -> System.out.println("onNext"));
与上一篇create操作符的使用代码相比少了很多,原因是使用链式写法和lambda表达式。create操作符的事件源还是一个ObservableOnSubscribe对象,但是订阅的不再是observer,而是一个Consumer,这个后面文章会讲解。
subscribeOn操作符需要传一个scheduler用于指定线程,然后创建一个被观察者:ObservableSubscribeOn和一个观察者SubscribeOnObserver。 ObservableSubscribeOn的source为上一个observable,核心代码在subscribeActual()方法中,源码如下:
@Override
public void subscribeActual(final Observer<? super T> s) {
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
s.onSubscribe(parent);
parent.setDisposable(scheduler.scheduleDirect(new Runnable() {
@Override
public void run() {
source.subscribe(parent);
}
}));
}
subscribeOn操作符是改变事件源的线程,即设置上一个observable.subscribe的线程。subscribeActual方法中不是直接调用source.subscribe(parent)而是通过传入的scheduler调用。由于传入的scheduler指定了新的线程,那么scheduler内部会切换到指定线程调用source.subscribe(parent);实现切换线程。
下面来看看scheduler内部是如何实现线程切换的。本篇以新线程Schedulers.newThread()为例进行分析,其他线程模式以及对比分析后面会单独写一篇。Scheduler通过入口方法scheduleDirect传入一个runable接口,通过runable的run方法调用source.subscribe(parent);,scheduleDirect内部实际是调用如下的重载方法:
public Disposable scheduleDirect(Runnable run, long delay, TimeUnit unit) {
final Worker w = createWorker();
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
w.schedule(new Runnable() {
@Override
public void run() {
try {
decoratedRun.run();
} finally {
w.dispose();
}
}
}, delay, unit);
return w;
}
该方法调用了createWorker()获得一个Worker,然后通过调用worker的schedule方法来调用传入的runable接口方法。createWorker是个抽象方法,需要子类重新。Schedulers.newThread()得到的实际是个Scheduler子类对象NewThreadScheduler。该类重新了createWorker并得到一个Worker的子类NewThreadWorker。那么真正的线程切换就是靠Worker来控制的。NewThreadWorker的schedule方法实际调用的是scheduleActual方法,NewThreadWorker的scheduleActual源码如下:
public ScheduledRunnable scheduleActual(final Runnable run, ...) {
Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);
//...
try {
if (delayTime <= 0) {
f = executor.submit((Callable<Object>)sr);
} else {
f = executor.schedule((Callable<Object>)sr, delayTime, unit);
}
sr.setFuture(f);
}
catch (RejectedExecutionException ex) {
//...
}
return sr;
}
方法内会使用线程池executor.submit((Callable<Object>)sr)去执行传入的runable接口方法,调用订阅方法,实现线程切换。整体流程线程变换如下图所示:
注意以下两点:
1.根据ObservableSubscribeOn 的subscribeActual源码可知,s.onSubscribe(parent)在线程切换前执行,那么onSubscribe不受subscribeOn线程切换影响,在原线程执行;
2.在使用subscribeOn操作符切换到新线程后,上游的所有subscribe订阅方法都会在新线程执行,一直穿透到事件源。如果整链式操作中,上游有新的subscribeOn操作符,那么线程又会改变,因此多个subscribeOn后,只有第一个会对事件源线程有效。