RxJava 线程切换原理

 

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 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 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 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)sr);
            } else {
                f = executor.schedule((Callable)sr, delayTime, unit);
            }
            sr.setFuture(f);
        ...
        return sr;
    } 
  

有没有一种很熟悉的感觉呢?

如果定时任务就交给了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。

你可能感兴趣的:(Android,Java)