subscribeOn操作符
上一节我们分析了observerOn操作符,它是可以切换onNext,onComplete所在线程的,也就是事件消费的线程;这一节我们来分析subscribeOn操作符,这个操作符是用来切换createEmitter类的subscribe方法的线程的,也就是切换事件发射所在的线程。
demo实例
按照惯例还是用一个demo代码来说明我们的subscribeOn操作符的作用以及用法。代码如下:
Log.e(TAG,"主线程ID:"+Thread.currentThread().getId());
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
Log.e(TAG,"subscribe线程ID:"+Thread.currentThread().getId());
emitter.onNext(1);
emitter.onComplete();
}
})
.subscribeOn(Schedulers.io())
//.observeOn(Schedulers.io())
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
dp = d;
Log.e(TAG,"onSubscribe线程ID:"+Thread.currentThread().getId());
}
@Override
public void onNext(Integer integer) {
Log.e(TAG,"onNext线程ID:"+Thread.currentThread().getId());
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "debounce:onError == " + e.getMessage());
}
@Override
public void onComplete() {
Log.e(TAG,"onComplete线程ID:"+Thread.currentThread().getId());
}
});
打印的结果如下:
16:54:15.865 28626-28626/cn.com.egova.test E/MainActivity: 主线程ID:2
16:54:15.893 28626-28626/cn.com.egova.test E/MainActivity: onSubscribe线程ID:2
16:54:15.896 28626-28661/cn.com.egova.test E/MainActivity: subscribe线程ID:1335
16:54:15.896 28626-28661/cn.com.egova.test E/MainActivity: onNext线程ID:1335
16:54:15.896 28626-28661/cn.com.egova.test E/MainActivity: onComplete线程ID:1335
可以看到onSubscribe所在的线程就是主线程,其他步骤都是在子线程中,符合预期,那我们在配合observerOn操作符看下,咱们把注释打开,看下线程是怎么切换的:
16:58:37.538 29005-29005/cn.com.egova.test E/MainActivity: 主线程ID:2
16:58:37.588 29005-29005/cn.com.egova.test E/MainActivity: onSubscribe线程ID:2
16:58:37.590 29005-29052/cn.com.egova.test E/MainActivity: subscribe线程ID:1348
16:58:37.592 29005-29053/cn.com.egova.test E/MainActivity: onNext线程ID:1349
16:58:37.593 29005-29053/cn.com.egova.test E/MainActivity: onComplete线程ID:1349
符合我们的预期,subscribe方法是事件发射方法,线程被切换成指定线程1348;onNext和onComplete方法是消费事件方法,线程被切换成指定线程1349,完美。
现在我们来直接剖析源码看看subscribeOn方法是怎么实现的我们的线程切换。
@CheckReturnValue
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable<T> subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
}
又是一样的代码,都看腻了对吗,🤭,创建一个被观察者,将上游和创建的调度器作为参数传递进去,接着进去看看是个啥:
public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
final Scheduler scheduler;
public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
super(source);
this.scheduler = scheduler;
}
@Override
public void subscribeActual(final Observer<? super T> observer) {
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(observer);
observer.onSubscribe(parent);
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
final class SubscribeTask implements Runnable {
private final SubscribeOnObserver<T> parent;
SubscribeTask(SubscribeOnObserver<T> parent) {
this.parent = parent;
}
@Override
public void run() {
source.subscribe(parent);
}
}
从这里我们发现一个细节就是actual方法直接调用了下游的观察者的onSubscribe方法,剩下的还是source.subscribe(parent);
流程不变。脑袋一拍不对啊,咱们的createEmitter不是也调用了嘛,这不就调用了两次这个方法,但是日志显示却只有一次,这是为啥?
static final class SubscribeOnObserver<T> extends AtomicReference<Disposable>
implements Observer<T>, Disposable {
@Override
public void onSubscribe(Disposable d) {
DisposableHelper.setOnce(this.upstream, d);
}
看到没,就是内部封装的观察者在传递给上游之后,观察者的onSubscribe方法调用链走到这一步之后并没有直接传递下去,而是将其封装起来作为内部upStream变量保存起来。这样咱们就明白了,onSubscribe方法只会被调用一次,第二次在这里就中断了,按照代码的顺序来看,actual中的先执行,后面是线程调度的,那么第二次到这里应该时间节点更晚。我没有对源码打日志,但是自己调试的结果,也是和自己预料的是一致的,大家有兴趣可以自己去调试下。
如何切换的线程
先画个类图,看下操作符内各个类之间的关系:
final class SubscribeTask implements Runnable {
private final SubscribeOnObserver<T> parent;
SubscribeTask(SubscribeOnObserver<T> parent) {
this.parent = parent;
}
@Override
public void run() {
source.subscribe(parent);
}
}
咱们的observer,被封装成SubscribeOnObserver之后,使用schedule.scheduleDirect()去直接调度SubscribeTask任务,任务的run函数执行source.subscribe(parent);很明显这句话就决定了上游执行subscribe方法所在的线程了,这就很好解释了observer.onSubscribe(parent);
为啥需要提前执行,因为observableCreate的subscribe方法内调用了onSubscribe方法,已经没办法再主线程执行了,所以需要这个办法在主线程中执行。画了一张代码调用链的图,大概就是这个样子:
注意:线程A和操作符A没有任何关系,A仅仅代表subscribeOn操作符定义的调度线程。
举一反三:假设我们在A,B之间在加上一个subscribeOn操作符D的话,假设D定义的调度线程是X,那么我们可以知道事件发射源发射事件所在的线程是线程X。
结论:综合上一章所说的observerOn操作符和这一章的subscribeOn操作符,我们可以总结下,在图中来看observerOn操作符影响的是下面的那一条“事件分发过程”泳道,而subscribeOn操作符影响的是上面那一条“订阅过程”泳道,得出的结论就是被观察者的发射事件所在的线程取决于下游离它最近的subscribeOn操作符。
好了subscribeOn操作符分析完了,但是感觉还是差点什么,哦,线程切换我们搞清楚了,但是这个线程本身我们并没有分析,下一章,我将剖析rxJava线程调度器是怎么实现的。