RxJava 2.x 源码分析(二) 线程切换原理

在RxJava的使用过程中,通常会用subscribeOn来指定可观察者Observable的线程,用observerOn来指定观察者Observer的线程。本文将分析RxJava2.x的线程切换原理

先来看一个例子

Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<Integer> e) throws Exception {
                mRxOperatorsText.append("Observable emit 1" + "\n");
                Log.d(TAG, "Observable emit 1" + "\n");
                e.onNext(1);
                mRxOperatorsText.append("Observable emit 2" + "\n");
                Log.d(TAG, "Observable emit 2" + "\n");
                e.onNext(2);
                mRxOperatorsText.append("Observable emit 3" + "\n");
                Log.d(TAG, "Observable emit 3" + "\n");
                e.onNext(3);
                //e.onComplete();
                mRxOperatorsText.append("Observable emit 4" + "\n");
                Log.d(TAG, "Observable emit 4" + "\n" );
                e.onNext(4);
                Log.d(TAG, "subscribe: ThreadName="+Thread.currentThread().getName());
            }
        }).subscribeOn(Schedulers.io()) //将发射者切换到io线程
                .observeOn(AndroidSchedulers.mainThread()) //将接收者切换到主线程
                .subscribe(new Observer<Integer>() {
            private int i;
            private Disposable mDisposable;

            @Override
            public void onSubscribe(@NonNull Disposable d) {
                mRxOperatorsText.append("onSubscribe : " + d.isDisposed() + "\n");
                Log.d(TAG, "onSubscribe : " + d.isDisposed() + "\n" );
                mDisposable = d;
                Log.d(TAG, "onSubscribe: ThreadName="+Thread.currentThread().getName());
            }

            @Override
            public void onNext(@NonNull Integer integer) {
                mRxOperatorsText.append("onNext : value : " + integer + "\n");
                Log.d(TAG, "onNext : value : " + integer + "\n" );
                Log.d(TAG, "onNext: ThreadName="+Thread.currentThread().getName());
                i++;
                if (i == 2) {
                    // 在RxJava 2.x 中,新增的Disposable可以做到切断的操作,让Observer观察者不再接收上游事件
                    mDisposable.dispose();
                    mRxOperatorsText.append("onNext : isDisposable : " + mDisposable.isDisposed() + "\n");
                    Log.d(TAG, "onNext : isDisposable : " + mDisposable.isDisposed() + "\n");
                }
            }

            @Override
            public void onError(@NonNull Throwable e) {
                mRxOperatorsText.append("onError : value : " + e.getMessage() + "\n");
                Log.d(TAG, "onError : value : " + e.getMessage() + "\n" );
            }

            @Override
            public void onComplete() {
                mRxOperatorsText.append("onComplete" + "\n");
                Log.d(TAG, "onComplete" + "\n" );
            }
        });

输出结果如下

如果不切换线程则输出结果如下

切换线程的情况下, 可以指定Observable的操作在IO线程中执行,Observer操作在主线程中执行,如果不切换线程的话,则默认都是主线程

在讲RxJava的线程切换之前,先来梳理一下RxJava框架的整人执行流程是怎么样的

一、RxJava的架构流程分析

下面是我画的一个RxJava的流程图,可能不是很严谨,但这样的流程图对源码的阅读和理解有很大的帮助

 1.首先是一个原始可观察者,它持有一个数据发射源,然后生成第二级、第三级……第n级等一系列可观察者,每个可观察者都持有上一级可观察者的引用,每个可观察者里面都可以对上一级可观察者进行相关处理,当然也可以只有一个可观察者即原始可观察者

2.当完成可观察者的生成之后,就开始执行了,从生成的最后一个可观察者开始,依次执行每个可观察者的相关处理,同时还会从一个原始观察者开始生成一系列的观察者

3.当程序执行到原始可观察者的时候,就会触发数据发射源发射数据,然后第n级观察者就首先收到数据,同样是依次执行所有的观察者,每个观察者都可以对接收到的数据进行处理,然后传给它的下一级观察者,最后到原始观察者接收到数据,从而结束整个执行

下面我们就从源码的角度来看一下RxJava的线程切换原理

 

二:Observable的线程切换原理

从方法subscribeOn(Schedulers.io())入手

    public final Observable<T> subscribeOn(Scheduler scheduler) {
        ObjectHelper.requireNonNull(scheduler, "scheduler is null");
        return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
    }

方法接收一个Scheduler参数,按理解这是一个线程调度器,返回一个ObservableSubscribeOn对象,这是一个可观察者,可见这里创建了一个新的可观察者,注意构造方法第一个参数传递this,即当前Observable本身,可知新的可观察者持有了上一个可观察者的引用

来看一下ObservableSubscribeOn类

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> s) {
        //创建新的观察者
        final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
        //作为Disposable传参
        s.onSubscribe(parent);
        //利用scheduler.scheduleDirect(new SubscribeTask(parent))进行线程切换
        parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
    }
...
}

在新的可观察者里面,其中subscribeActual方法是执行当前可观察者的的意思,也是我们重点关注的方法,里面会生成一个新的观察者,然后通过线程调度器scheduler切换下一个可观察者的执行线程,先来看看任务类SubscribeTask

 

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

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

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

上面的run方法将在一个指定的线程中执行,在新线程中调用下一级可观察者source的执行方法subscribe,同时把新的观察者传参过去,这就实现了可观察者的线程切换

下面就来看看这个SubscribeTask是如何在指定线程中执行的,scheduleDirect是零延时执行的意思

    public Disposable scheduleDirect(@NonNull Runnable run) {
        return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
    }
    
    public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
        //这是一个抽象方法,要找到它的实现类
        final Worker w = createWorker();

        final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
        //DisposeTask 实现了Disposable接口
        DisposeTask task = new DisposeTask(decoratedRun, w);
        //通过Worker来执行
        w.schedule(task, delay, unit);

        return task;
    }

createWorker是一个抽象方法,因为这里的线程调度器是Schedulers.io(),所以找到了IoScheduler,来看看它的createWorker方法

  public Worker createWorker() {
        return new EventLoopWorker(pool.get());
    }

 里面关于线程的调度有点复杂,主要是通过线程池来执行任务的,细节就不研究了,来看EventLoopWorker的schdule方法

        public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
            if (tasks.isDisposed()) {
                // don't schedule, we are unsubscribed
                return EmptyDisposable.INSTANCE;
            }

            return threadWorker.scheduleActual(action, delayTime, unit, tasks);
        }

先判断任务有没有被中断,最终由threadWorker来执行任务

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

        if (parent != null) {
            if (!parent.add(sr)) {
                return sr;
            }
        }

        Future<?> f;
        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) {
            if (parent != null) {
                parent.remove(sr);
            }
            RxJavaPlugins.onError(ex);
        }

        return sr;
    }

 

 三、Observer的线程切换原理

从上面得知,可观察者的线程切换,是通过将下一级的可观察者的执行放入到指定线程中来实现的;所以我们可以猜想,观察者的线程切换,应该也是通过将下一级的观察者的执行放入到指定线程中来实现的

下面从observerOn方法开始

        public final Observable<T> observeOn(Scheduler scheduler) {
        return observeOn(scheduler, false, bufferSize());
    }

        public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
        ObjectHelper.requireNonNull(scheduler, "scheduler is null");
        ObjectHelper.verifyPositive(bufferSize, "bufferSize");
        return RxJavaPlugins.onAssembly(new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize));
    }

可以看到,返回一个ObservableObserverOn实例,同样是生成一个新的可观察者

    public ObservableObserveOn(ObservableSource<T> source, Scheduler scheduler, boolean delayError, int bufferSize) {
        super(source);
        this.scheduler = scheduler;
        this.delayError = delayError;
        this.bufferSize = bufferSize;
    }

其中Scheduler为observerOn方法中传递的线程调度器,我们还是来关注可观察者的执行方法subscribeActual,一般会在这个方法中创建一个新的观察者

    @Override
    protected void subscribeActual(Observer<? super T> observer) {
        if (scheduler instanceof TrampolineScheduler) {
            source.subscribe(observer);
        } else {
            //这个worker来切换线程
            Scheduler.Worker w = scheduler.createWorker();

            source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
        }
    }

可以看到,在执行下一个可观察者的subscribe方法的时候,传入一个新的观察者ObserveOnObserver,这个新的观察者就是用来切换线程的,来看看它是怎么切换线程的,分析其中的onNext方法

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

            if (sourceMode != QueueDisposable.ASYNC) {
                //存入队列
                queue.offer(t);
            }
            schedule();
        }

        @Override
        public void onError(Throwable t) {
            if (done) {
                RxJavaPlugins.onError(t);
                return;
            }
            error = t;
            done = true;
            schedule();
        }

        @Override
        public void onComplete() {
            if (done) {
                return;
            }
            done = true;
            schedule();
        }

done代表工作有没有完成的意思,在onNext方法中先将当前观察者收到的数据保存到一个队列中,然后调用schedule方法

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

同样,worker是我们在observerOn中指定的线程调度器,传参this,所以去看看类中run方法的实现

        @Override
        public void run() {
            if (outputFused) {
                drainFused();
            } else {
                //执行这里
                drainNormal();
            }
        }

这里就是线程切换的关键了

        void drainNormal() {
            int missed = 1;

            final SimpleQueue<T> q = queue;
            //下一级观察者
            final Observer<? super T> a = actual;
            //死循环
            for (;;) {
                //如果被中止了,则跳出
                if (checkTerminated(done, q.isEmpty(), a)) {
                    return;
                }
                //死循环
                for (;;) {
                    //是否完成
                    boolean d = done;
                    T v;

                    try {
                        //从队列中取出接收到的数据T
                        v = q.poll();
                    } catch (Throwable ex) {
                        Exceptions.throwIfFatal(ex);
                        s.dispose();
                        q.clear();
                        a.onError(ex);
                        worker.dispose();
                        return;
                    }
                    //队列是否为空
                    boolean empty = v == null;
                    //再次检查任务有没有被中止
                    if (checkTerminated(d, empty, a)) {
                        return;
                    }
                    //如果队列为空,跳出当前循环
                    if (empty) {
                        break;
                    }
                    //将数据交接给下一级观察者
                    a.onNext(v);
                }

                missed = addAndGet(-missed);
                if (missed == 0) {
                    break;
                }
            }
        }

观察者线程的切换,首先是将当前观察者接收到的数据保存在一个队列中,然后在我们指定的线程内,从队列中的取出数据,传给下一个观察者,从而实现了线程的切换

到这里,我们理解了RxJava的线程切换原理,下一篇将分析有关变换操作符的原理

下一篇:RxJava 2.x 源码分析(三) 变换操作符原理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值