RxJava2.1.14源码学习(三)线程切换observerOn

本文深入探讨RxJava中的observerOn操作符,分析其如何影响事件流,以及多次使用和与subscribeOn的混合使用的效果。内容包括observerOn的实现原理,单个与多个observerOn的执行逻辑,并通过示例代码解释其线程切换行为。
摘要由CSDN通过智能技术生成

上一篇我们已经介绍了线程切换两大操作符中的subscribeOn了,这一片我们来分析一下observerOn。本文重点是:
(1)observeOn是如何实现的,它是如何影响事件流的,
(2)如果我们使用多次会是什么结果,为什么?
(3)subscribeOn和observeOn的混合使用效果

如果对整体流程还不清晰的最好先去看看前面两篇:

RxJava2.1.14源码学习(一)基本流程(附带装饰者模式、观察者模式说明)
RxJava2.1.14源码学习(二)线程切换subscribeOn

单个ObserveOn

先从单个observeOn开始吧,下面是示例代码和结果:

 Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                LogUtils.e(TAG, "subscribe:" + Thread.currentThread().getName());
                emitter.onNext(1 + "");
                emitter.onComplete();
            }
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        LogUtils.e(TAG, "onSubscribe:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onNext(String result) {
                        //收到的值类型发生变化
                        LogUtils.e(TAG, "onNext:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onError(Throwable e) {
                        LogUtils.e(TAG, "onError:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onComplete() {
                        LogUtils.e(TAG, "onComplete:" + Thread.currentThread().getName());
                    }
                });

单个observeOn
发现除了subscribe,其它都是在主线程。前面的流程就不在分析了,我们从observeOn开始,我们猜测应该是新建了一个ObservableObserveOn类,然后里面的重点是重写的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));
    }
}

我们这里传入的是AndroidSchedulers.mainThread(),它的本质就是一个HandlerScheduler,至于为什么是,怎么找的,我们在介绍subscribeOn已经具体分析如何找到对应的Scheduler对象了,这里就不在说了,如果不懂可以去上一篇看一下。因为HandlerScheduler并不是TrampolineScheduler实例,所以走else中的逻辑,好像也没有什么特别的,获取一个Scheduler.Worker对象,这里是HandlerWorker实例,它的成员变量是一个在主线程的Handler对象,然后依然是正常的上游的被观察者对象订阅当前包装了下游观察者的新观察者。

然后从observeOn继续往上游到subscribeOn,我们知道他会触发onSubscribe函数,至于之前的都会被截断,就不管了,我们看下被回调的ObserveOnObserver的onSubscribe函数:

@Override
public void onSubscribe(Disposable s) {
     if (DisposableHelper.validate(this.s, s)) {
         this.s = s;
         ...
         //因为s并不是QueueDisposable实例,所以上面我们就不管了
         //这里实例化了一个队列
         queue = new SpscLinkedArrayQueue<T>(bufferSize);
         //回调下游观察者,即Observer的onSubscribe
         actual.onSubscribe(this);
     }
}

从上面的逻辑可以看出,此时onSubscribe还是运行在其它事件之前,所以依然是在主线程(即发生在订阅的过程中,而不是事件发射时)。接下来我们直接到上游源头去看事件的发射就可以了,并没有什么其它需要关注的逻辑。在源头,我们发射了以下事件:

 @Override
 public void subscribe(ObservableEmitter<String> emitter) throws Exception {
     LogUtils.e(TAG, "subscribe:" + Thread.currentThread().getName());
     emitter.onNext(1 + "");
     emitter.onComplete();
}

首先我们在发射事件之前打了一个日志(即打印出来的subscribe),根据上面的结果,当前是处于IO的子线程中,这是可以理解的,然后就是发射onNext和onComplete了,正常来说它们应该也是在IO的子线程,可是现在不是,是在主线程,我们猜测是不是在回调到ObserveOnObserver时作了什么处理,我们去看一下,它关于onNext和onComplete的实现:

@Override
public void onNext(T t) {
    //控制不二次执行
    if (done) {
         return;
    }
    //默认是0,不等于 QueueDisposable.ASYNC,所以将发射的T对象入队
    if (sourceMode != QueueDisposable.ASYNC) {
         queue.offer(t);
    }
    //重点
    schedule();
}

@Override
public void onComplete() {
    if (done) {
         return;
    }
    //次重点,后面分析onComplete时有用
    done = true;
    //重点
    schedule();
}

void schedule() {
    if (getAndIncrement() == 0) {
        //重点(参数为Runnable对象)
        worker.schedule(this);
    }
}

在回调的onNext和onComplete都没有做什么特殊处理,不过它们都调用了schedule,因为AtomicInteger的VALUE值,默认是0,我们还没有进行任何操作,所以会走到里面worker.schedule,这里的worker是HandlerWorker,我们去看一下它的schedule:

@Override
public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
    ...
    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));

    // Re-check disposed state for removing in case we were racing a call to dispose().
    if (disposed) {
        handler.removeCallbacks(scheduled);
        return Disposables.disposed();
    }

    return scheduled;
}

我想看到这里,大家可能就明白了吧,因为看到了熟悉的handler(前面我们说了,它是一个在主线程的Handler)、Message,把当前Runnable发送到轮训器里面,取出执行其run方法,所以onNext和onComplete都是在主线程回调的。不信我们继续去看一下,我们现在要关注的是ObserveOnObserverrun方法,因为我们上面把它的封装对象传入的轮训器中:

@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 {
                 v = q.poll();
             } catch (Throwable ex) {
                ...
             }
             ...

             a.onNext(v);
         }

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

我们发现drainNormal就是从队列中取出参数T,然后做了一些检查,最后调用其onNext,而此时,因为经由Hanlder回调在主线程了。不过你可能会感到奇怪,这里并没有onErroronComplete的逻辑啊,不要急,我们回想一下刚才ObserveOnObserveronComplete方法,它里面主要做了两个操作:
(1)将done置为true
(2)同样回调schedule
同理会回调到drainNormal,不过有一段代码我们刚才没有去关注:

if (checkTerminated(done, q.isEmpty(), a)) {
    return;
}

此时done为true,onNext的任务队列(即q)为empty,因为上一步因为全部循环取出回调了,那现在我们进去checkTerminated看一下:

boolean checkTerminated(boolean d, boolean empty, Observer<? super T> a) {
            if (cancelled) {
                queue.clear();
                return true;
            }
            //d为done,即true
            if (d) {
                Throwable e = error;
                //delayError为false
                if (delayError) {
                    if (empty) {
                        if (e != null) {
                            a.onError(e);
                        } else {
                            a.onComplete();
                        }
                        worker.dispose();
                        return true;
                    }
                } else {
                    //此处我们回调的是onComplete,所以e为null
                    if (e != null) {
                        queue.clear();
                        a.onError(e);
                        worker.dispose();
                        return true;
                    } else
                    if (empty) {
                        //因为此时onNext任务队列为空,所以走到这
                        a.onComplete();
                        worker.dispose();
                        return true;
                    }
                }
            }
            return false;
        }

到现在已经很明朗了,执行完全部的onNext,在回调onComplete时,返回true,所以drainNormal后面相关代码就不再执行,因为已经return了。onError也是同样的道理。同时,我们在上面代码中还发现一个问题,那就是我一回调完onComplete就把worker给dispose了,所以后面如果我们继续调用onError就不会继续执行了,因为已经停止订阅。

到这里我们就可以总结一下subscribeOnobserverOn的使用:
(1)subscribeOn只对上游有效,因为是在订阅过程中传递的,如果有多个,那么只有第一个”生效”(其实对于传递订阅关系都生效了,只是最终事件发射只体现出了最上游subscribeOn的作用)
(2)observerOn只对下游有效,因为它是在事件发射出来之后,回调事件的过程中生效的

多个observerOn

在上面我们已经总结出来了规律,那我们现在来试一试多个observerOnsubscribeOn的混合使用,并根据上面的总结作一下推测,最后运行示例代码,查看结果是否一致。

Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {

                LogUtils.e(TAG, "subscribe:" + Thread.currentThread().getName());
                emitter.onNext(1 + "");
                emitter.onComplete();
            }
        })
                //subscribeOn1
                .subscribeOn(Schedulers.io())
                //observeOn1
                .observeOn(AndroidSchedulers.mainThread())
                .map(new Function<String, Integer>() {
                    @Override
                    public Integer apply(String s) throws Exception {
                        return Integer.parseInt(s);
                    }
                })
                //subscribeOn2
                .subscribeOn(Schedulers.newThread())
                .filter(new Predicate<Integer>() {
                    @Override
                    public boolean test(Integer integer) throws Exception {
                        return integer == 1;
                    }
                })
                 //observeOn2
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Integer>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        LogUtils.e(TAG, "onSubscribe:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onNext(Integer result) {
                        //收到的值类型发生变化
                        LogUtils.e(TAG, "onNext:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onError(Throwable e) {
                        LogUtils.e(TAG, "onError:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onComplete() {
                        LogUtils.e(TAG, "onComplete:" + Thread.currentThread().getName());
                    }
                });
    }

示例代码结合上一篇和上面关于单个observerOn的分析,我们分析:最后结果和上面单个observerOn是一致的,我们来看下结果:
多s多o1
果然是一样的,我想这个大家也很容易理解吧,subscribeOn在订阅时发起,影响事件发射时所在的线程,因为有多个,所以只有第一个真正影响到了事件所在线程,因此subscribe打印所在线程为IO线程是正常的,而当onNext和onComplete最终传递给Observer时,因为最终作了一个observeOn在主线程的操作,所以这两个回调都在主线程,至于onSubscribe是临Observer最近的生效,其它全部截断,因为我们是在主线程发起的,所以这里也是在主线程。

到这里关于线程切换的操作符分析已经完成。(#^.^#)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值