浅析RxJava2线程切换

RxJava2中有两个可以切换线程的操作符,subscribeOn和observeOn。它们有什么区别,如果分别使用多次,会是什么结果。
先说结论:
observeOn() 关注的是在它之后的操作,调用多次则切换多次线程。
subscribeOn() 只有最开始的一次起作用,如果调用多次,只是创建了线程,它去通知它的上游的动作是在这个线程里,但最后onXxx()的动作还是在第一次调用的线程里。

// ObservableRange
Observable.range(1, 10)
        // ObservableMap
        .map(intValue -> String.valueOf(intValue))
        // ObservableSubscribeOn
        .subscribeOn(Schedulers.io())
        // ObservableObserveOn
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(System.out::println);

这是一段简单的io线程创建事件流,主线程消费处理的代码。他们之间是怎么做到线程切换的,这是今天要找到的答案。我在每一行操作符上注释了每个操作符对应生成的Observable,所以最后调用subscribe的是一个ObservableObserveOn对象,他们的subscribe方法都在父类Observable中。

@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public final Disposable subscribe(Consumer<? super T> onNext) {
    return subscribe(onNext, Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION, Functions.emptyConsumer());
}
    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.NONE)
    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError,
            Action onComplete, Consumer<? super Disposable> onSubscribe) {
        // 省略非空判断
        LambdaObserver<T> ls = new LambdaObserver<T>(onNext, onError, onComplete, onSubscribe);

        subscribe(ls);

        return ls;
    }

可以看到,最后会创建一个LambdaObserver,继续调用subscribe

    @SchedulerSupport(SchedulerSupport.NONE)
    @Override
    public final void subscribe(Observer<? super T> observer) {
        ObjectHelper.requireNonNull(observer, "observer is null");
        try {
            observer = RxJavaPlugins.onSubscribe(this, observer);

            ObjectHelper.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null Observer. Please change the handler provided to RxJavaPlugins.setOnObservableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins");

            subscribeActual(observer);
        } catch (NullPointerException e) { // NOPMD
            throw e;
        } catch (Throwable e) {
           // 省略异常处理
        }
    }

关键的地方来了,这里调用了抽象方法subscribeActual,ObservableObserveOn的subscribeActual实现如下:

    @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<T>(observer, w, delayError, bufferSize));
        }
    }

这里会判断scheduler的类型,这个成员变量在构造ObservableObserveOn的时候传入,传入的是AndroidSchedulers.mainThread(),是一个HandlerScheduler对象,所以subscribeActual中会进入else继续执行。调用scheduler的抽象方法createWorker创建Scheduler.Worker对象。HandlerScheduler的实现很简单,只是创建HandlerWorker对象:

    @Override
    public Worker createWorker() {
        return new HandlerWorker(handler, async);
    }

其中handler就是android中线程调用中的Handler,它使用new Handler(Looper.getMainLooper())中创建,async是false。可见RxAndroid中的切换到main线程使用的还是Handler,流水的开源库,铁打的Handler。最后调用上游的subscribe,传入一个新创建的ObserveOnObserver。此例中上游是一个ObservableSubscribeOn对象,接着调用它的subscribeActual。

    @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)));
    }

同样创建一个Observer,从ObservableObserveOn中传进来的ObserveOnObserver是它的下游,scheduler是一个IoScheduler对象,会调用它的下面这个方法:

    @NonNull
    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;
    }

IoScheduler对应的Worker实现是EventLoopWorker,它会立即提交执行SubscribeTask,它只是再次调用了上游的subscribe

   final class SubscribeTask implements Runnable {
        private final SubscribeOnObserver<T> parent;

        SubscribeTask(SubscribeOnObserver<T> parent) {
            this.parent = parent;
        }

        @Override
        public void run() {
            source.subscribe(parent);
        }
    }

同理ObservableMap也会调用它的上游并且传入自己的Observer,如此往上一直调用,直到最上游,本例中是ObservableRange,看下它的subscribeActual

    @Override
    protected void subscribeActual(Observer<? super Integer> o) {
        RangeDisposable parent = new RangeDisposable(o, start, end);
        o.onSubscribe(parent);
        parent.run();
    }

创建一个RangeDisposable对象,传入的Observer作为它的下游,调用它的run方法

        void run() {
            if (fused) {
                return;
            }
            Observer<? super Integer> actual = this.downstream;
            long e = end;
            for (long i = index; i != e && get() == 0; i++) {
                actual.onNext((int)i);
            }
            if (get() == 0) {
                lazySet(1);
                actual.onComplete();
            }
        }

在这里会再去调用下游的onNext,下游是ObservableMap里的MapObserver,它在自己的onNext中又去调用它的下游的onNext,
这里是SubscribeOnObserver,要看一下:

        @Override
        public void onNext(T t) {
            downstream.onNext(t);
        }

它也只是简单调用下游的onNext()而已,所以如果调用了多次这个方法,相当于下面这段代码:

        new Thread(new Runnable() {
            @Override
            public void run() {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // onNext();
                        // onComplete/onError
                    }
                }).start();
            }
        }).start();

从下游到上游,从外层到内层,每调用一次就创建一个线程,但是只有最里层的线程也就是最上游的subscribeOn调用起作用,因为onNext,onComplete/onError是在这里调用的。接着在本例中的最后一个下游ObservableObserveOn,对应的Observer是ObserveOnObserver:

        @Override
        public void onNext(T t) {
            if (done) {
                return;
            }

            if (sourceMode != QueueDisposable.ASYNC) {
                queue.offer(t);
            }
            schedule();
        }

它之所以可以每调用一次就切换一次线程的原因是,它会在onNext,onComplete/onError中调用schedule

        void schedule() {
            if (getAndIncrement() == 0) {
                worker.schedule(this);
            }
        }

它会在这里执行线程切换的动作,所以可以每次调用都起作用。

        @Override
        public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
            if (disposed) {
                return Disposables.disposed();
            }
            ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);

            Message message = Message.obtain(handler, scheduled);
            message.obj = this; // Used as token for batch disposal of this worker's runnables.
            handler.sendMessageDelayed(message, unit.toMillis(delay));
            return scheduled;
        }

通过handler发送一个延时消息,只不过delay为0,这样就执行在Handler所在的线程了,其他线程的切换也类似。

猜你喜欢

转载自blog.csdn.net/jthou20121212/article/details/82958098