RxJava的线程切换主要涉及到
observeOn(),subscribeOn()
我们来分析一下这两个方法是怎么做到切换的。
observeOn()作用于上一个构造好的Observable实例,RxJava设计比较巧妙的地方是,把线程切换的操作也封装成了Observable放在Observable subscribe()方法和Observer onNext()执行链路中。
先分析subscribeOn()
public final Observable subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn(this, scheduler));
}
new 了一个ObservableSubscribeOn对象,同时传入了当前节点作为previous 节点,scheduler作为要调度的调度器。
@Override
public void subscribeActual(final Observer super T> observer) {
final SubscribeOnObserver parent = new SubscribeOnObserver(observer);
observer.onSubscribe(parent);
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
关键代码就在于subscribeActual()中,把observer封装成一个Task后,调用了scheduler.scheduleDirect();
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
final Worker w = createWorker();
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
DisposeTask task = new DisposeTask(decoratedRun, w);
w.schedule(task, delay, unit);
return task;
}
scheduleDirect()做的事情是把传入的Runnable做了再次的封装并交给了worker来处理。我们现在不去研究具体什么样的线程池。现在只看怎么实现切换的。
刚才的线程调度发生在subscribeActual()方法中,但是我们知道subscribe()会不断地递归向上溯源,一直追到原点ObservableCreate中,调用ObservableOnsubScribe类中的subscribe()方法,也就是说,subscirbeOn()会影响到subscribe()中数据源产生的地方。如果这中间有多次执行subscribeOn()那么,由于中间尚未吐出数据,所以subscribeOn()只有在最靠近起点的地方有效,后面的多次subscribeOn()都无效。
我们再分析一下ObserveOn(),先在现有的observable链后加入一个新的节点ObservableObserveOn(),
public final Observable observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
ObjectHelper.verifyPositive(bufferSize, "bufferSize");
return RxJavaPlugins.onAssembly(new ObservableObserveOn(this, scheduler, delayError, bufferSize));
}
看看ObservableObserveOn中比较关键的几个方法。
@Override
protected void subscribeActual(Observer super T> observer) {
if (scheduler instanceof TrampolineScheduler) {
source.subscribe(observer);
} else {
Scheduler.Worker w = scheduler.createWorker();
source.subscribe(new ObserveOnObserver(observer, w, delayError, bufferSize));
}
}
可以看到其subscribeActual()方法中并未去做线程切换,只是构造了Scheduler.Worker,并把它传到了自己的ObservableOnObsever对象的构造函数的参数中,可以猜想到这样的目的是影响Observer中的onNext()执行所在的线程,这样可以影响到数据流的处理。我们顺着思路往下看:
static final class ObserveOnObserver extends BasicIntQueueDisposable
implements Observer, Runnable {
...
ObserveOnObserver(Observer super T> actual, Scheduler.Worker worker, boolean delayError, int bufferSize) {
this.worker = worker;
...
}
...
@Override
public void onNext(T t) {
...
schedule();
}
@Override
public void onError(Throwable t) {
...
schedule();
}
@Override
public void onComplete() {
...
schedule();
}
...
void schedule() {
if (getAndIncrement() == 0) {
worker.schedule(this);
}
}
}
果然,在其onNext(),onComplete(),onError()中都调用到了schedule()方法,由于ObserveOnObserver实现了Runnable接口,直接在worker.schedule()中把当前对象的实例传入。
下面以NewThreadWorker为例,分析一下丢给schedule做了一些什么事情。
public Disposable schedule(@NonNull final Runnable action, long delayTime, @NonNull TimeUnit unit) {
...
return scheduleActual(action, delayTime, unit, null);
}
public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);
...
Future> f;
...
if (delayTime <= 0) {
f = executor.submit((Callable
有没有一种很熟悉的感觉呢?
如果定时任务就交给了executor线程池的schedule方法处理,普通任务直接submit()。至此,提交给了具体的某个由Schedulers抽象类中的静态工厂生成的某个线程池。
由于ObserveOn()作用于数据从源头往下流淌的downstream过程,所以,拦截在这个过程中的线程切换就会生效,作用于当前任务中的onNext()等方法。
了解清楚了subscribeActual()的向上溯源和subscribe()后不断onNext()向下责任链,就不难理清楚RxJava的线程切换原理了。
下面举一个例子来说明一下:
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext("hello");
System.out.println("Thread:"+Thread.currentThread().getName());
}
}).observeOn(Schedulers.computation())
.subscribeOn(Schedulers.io())
.map(new Function() {
@Override
public String apply(String s) throws Exception {
System.out.println("Thread:"+Thread.currentThread().getName());
return "abc-"+s;
}
}).observeOn(Schedulers.newThread())
.map(new Function() {
@Override
public String apply(String s) throws Exception {
System.out.println("Thread:"+Thread.currentThread().getName());
return s+"-def";
}
}).observeOn(Schedulers.single())
.subscribeOn(Schedulers.single())
.subscribe(new Consumer() {
@Override
public void accept(String s) throws Exception {
System.out.println("Thread:"+Thread.currentThread().getName());
System.out.println("result:"+s);
}
});
这里面涉及到了多次线程切换,多次调用observeOn(),subscribeOn(),所以按照之前的分析,subscribeOn()只有离observable链表最原点的地方的切换才有效,所以这里只有第一个subscribeOn()有效,也就说数据应该从Schedulers.io()线程里面答应出来,后来的每次observeOn()切换都会改变onNext()也就是相应function()中的apply()所在执行线程,因为Function最后也会被封装成observer。
我们看看打印结果和我们的分析是否一致:
Thread:RxCachedThreadScheduler-1
Thread:RxComputationThreadPool-1
Thread:RxNewThreadScheduler-1
Thread:RxSingleScheduler-1
result:abc-hello-def
这里要说明的是Schedulers.io()所用到的ThreadFactory传入的标签是“RxCachedThreadScheduler”,所以会打印CachedThread。