迷之RxJava —— 线程切换

RxJava最迷人的是什么?
答案就是把异步序列写到一个工作流里!javascriptPromise/A如出一辙。
OK,在java中做异步的事情在我们传统理解过来可不方便,而且,如果要让异步按照我们的工作流来,就更困难了。

但是在RxJava中,我们只要调用调用
subscribOn()observeOn()就能切换我们的工作线程,是不是让小伙伴都惊呆了?

然后结合RxJavaOperator,写异步的时候,想切换线程就是一行代码的事情,整个workflow还非常清晰:

Observable.create()
// do something on io thread
.work() // work.. work..
.subscribeOn(Schedulers.io())
// observeOn android main thread
.observeOn(AndroidSchedulers.mainThread())
.subscribe();

我们再也不用去写什么见鬼的new ThreadHandler了,在这么几行代码里,我们实现了在io线程上做我们的工作(work),在main线程上,更新UI

Subscribe On

先看下subscribeOn干了什么

 public final Observable<T> subscribeOn(Scheduler scheduler) {
    if (this instanceof ScalarSynchronousObservable) {
        return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
    }
    return nest().lift(new OperatorSubscribeOn<T>(scheduler));
}

啊,原来也是个lift,就是从一个Observable生成另外一个Observable咯,这个nest是干嘛用?

 public final Observable<Observable<T>> nest() {
    return just(this);
}

这里返回类型告诉我们,它是产生一个Observable<Observable<T>>
讲到这里,会有点晕,先记着这个,然后我们看OperatorSubscribeOn这个操作符,

构造函数是

public OperatorSubscribeOn(Scheduler scheduler) {
    this.scheduler = scheduler;
}

OK,这里保存了scheduler对象,然后就是我们前一章说过的转换方法。

 @Override
public Subscriber<? super Observable<T>> call(final Subscriber<? super T> subscriber) {
    final Worker inner = scheduler.createWorker();
    subscriber.add(inner);
    return new Subscriber<Observable<T>>(subscriber) {

        @Override
        public void onCompleted() {
            // ignore because this is a nested Observable and we expect only 1 Observable<T> emitted to onNext
        }

        @Override
        public void onError(Throwable e) {
            subscriber.onError(e);
        }

        @Override
        public void onNext(final Observable<T> o) {
            inner.schedule(new Action0() {

                @Override
                public void call() {
                    final Thread t = Thread.currentThread();
                    o.unsafeSubscribe(new Subscriber<T>(subscriber) {

                        @Override
                        public void onCompleted() {
                            subscriber.onCompleted();
                        }

                        @Override
                        public void onError(Throwable e) {
                            subscriber.onError(e);
                        }

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

                        @Override
                        public void setProducer(final Producer producer) {
                            subscriber.setProducer(new Producer() {

                                @Override
                                public void request(final long n) {
                                    if (Thread.currentThread() == t) {
                                        // don't schedule if we're already on the thread (primarily for first setProducer call)
                                        // see unit test 'testSetProducerSynchronousRequest' for more context on this
                                        producer.request(n);
                                    } else {
                                        inner.schedule(new Action0() {

                                            @Override
                                            public void call() {
                                                producer.request(n);
                                            }
                                        });
                                    }
                                }

                            });
                        }

                    });
                }
            });
        }

    };
}

让人纠结的类模板

看完这段又臭又长的,先深呼吸一口气,我们慢慢分析下。
首先要注意RxJava里面最让人头疼的模板问题,那么OperatorMap这个类的声明是

public final class OperatorMap<TRimplements Operator<RT>

Operator这个接口继承Func1

public interface Func1<T, R> extends Function {
    R call(T t);
}

我们这里不要记TR,记住传入左边的模板是形参,传入右边的模板是返回值

好了,那么这里的call就是从一个T转换成一个Observable<T>的过程了。

总结一下,我们这一次调用subscribeOn,做了两件事

1、nest() 为Observable<T>生成了一个Observable<Observable<T>> 
2、lift() 对Observalbe<Observalbe<T>>进行一个变化,变回Observable<T>

因为lift是一个模板函数,它的返回值的类型是参照它的形参来,而他的形参是Operator<T, Observable<T>> 这个结论非常重要!!
OK,到这里我们已经存储了所有的序列,等着我们调用了。

调用链

首先,记录我们在调用这条指令之前的Observable<T>,记为Observable$1
然后,经过lift生成的Observable<T>记为Observable$2

好了,现在我们拿到的依然是Observable<T>这个对象,但是它不是原始的Observable$1,要深深记住这一点,它是由lift生成的Observable$2,这时候进行subscribe,那看到首先调用的就是OnSubscribe.call方法,好,直接进入lift当中生成的那个地方。

我们知道这一层liftoperator就是刚刚的OperatorSubscribOn,那么调用它的call方法,生成的是一个Subscriber<Observable<T>>

Subscriber<? super T> st = hook.onLift(operator).call(o);
try {
    // new Subscriber created and being subscribed with so 'onStart' it
    st.onStart();
    onSubscribe.call(st);
} catch (Throwable e) {
...
}

好,还记得我们调用过nest么?,这里的onSubscribe可是nest上下文中的噢,每一次,到这个地方,这个onSubscribe就是上一层ObservableonSubscribe,即Observable<Observable<T>>onSubscribe,相当于栈弹出了一层。它的call直接在SubscriberonNext中给出了最开始的Observable<T>,我们这里就要看下刚刚在OperatorSubscribeOn中生成的Subscriber

new Subscriber<Observable<T>>(subscriber) {

    @Override
    public void onCompleted() {
        // ignore because this is a nested Observable and we expect only 1 Observable<T> emitted to onNext
    }
    
    @Override
    public void onError(Throwable e) {
        subscriber.onError(e);
    }
    
    @Override
    public void onNext(final Observable<T> o) {
        inner.schedule(new Action0() {
    
            @Override
            public void call() {
                final Thread t = Thread.currentThread();
                o.unsafeSubscribe(new Subscriber<T>(subscriber) {
    
                    @Override
                    public void onCompleted() {
                        subscriber.onCompleted();
                    }
    
                    @Override
                    public void onError(Throwable e) {
                        subscriber.onError(e);
                    }
    
                    @Override
                    public void onNext(T t) {
                        subscriber.onNext(t);
                    }
                });
            }
        });
    }
}

对,就是它,这里要注意,这里的subscriber就是我们在lift中,传入的o

Subscriber<? super T> st = hook.onLift(operator).call(o);

对,就是它,其实它就是SafeSubscriber

回过头,看看刚刚的onNext()方法,inner.schedule() 这个函数,我们可以认为就是postRun()类似的方法,而onNext()中传入的o是我们之前生成的Observable$1,是从Observable.just封装出来的Observable<Observable<T>>中产生的,这里调用了Observable$1.unsafeSubscribe方法,我们暂时不关心它和subscribe有什么不同,但是我们知道最终功能是一样的就好了。

注意它运行时的线程!!在inner这个Worker上!于是它的运行线程已经被改了!!

好,这里的unsafeSubscribe调用的方法就是调用原先Observable$1.onSubscribe中的call方法:
这个Observable$1就是我们之前自己定义的Observable了。

综上所述,如果我们需要我们的Observable$1在一个别的线程上运行的时候,只需要在后面跟一个subscribeOn即可。结合扔物线大大的图如下:

总结

这里逻辑着实不好理解。如果还没有理解的朋友,可以按照我前文说的顺序,细致的看下来,我把逻辑过一遍之后,发现lift的陷阱实在太大,内部类用的风生水起,一不小心,就不知道一个变量的上下文是什么,需要特别小心。


之前我们分析过subscribeOn这个函数,
现在我们来看下subscribeOnobserveOn这两个函数到底有什么异同。

用过rxjava的旁友都知道,subscribeOnobserveOn都是用来切换线程用的,可是我什么时候用subscribeOn,什么时候用observeOn呢,我们很少知道这两个区别是啥。

友情提示,如果不想看分析过程的,可以直接跳到下面的总结部分。

subscribeOn

先看下OperatorSubscribeOn的核心代码:

public final class OperatorSubscribeOn<T> implements OnSubscribe<T> {

    final Scheduler scheduler;
    final Observable<T> source;

    public OperatorSubscribeOn(Observable<T> source, Scheduler scheduler) {
        this.scheduler = scheduler;
        this.source = source;
    }

    @Override
    public void call(final Subscriber<? super T> subscriber) {
        final Worker inner = scheduler.createWorker();
        subscriber.add(inner);
        
        inner.schedule(new Action0() {
        
            @Override
            public void call() {
               
                Subscriber<T> s = new Subscriber<T>(subscriber) {
                    @Override
                    public void onNext(T t) {
                        subscriber.onNext(t);
                    }
                    
                    @Override
                    public void onError(Throwable e) {
                        try {
                            subscriber.onError(e);
                        } finally {
                            inner.unsubscribe();
                        }
                    }
                    
                    @Override
                    public void onCompleted() {
                        try {
                            subscriber.onCompleted();
                        } finally {
                            inner.unsubscribe();
                        }
                    }
                    
                    ....
                };
                
                source.unsafeSubscribe(s);
            }
        });
    }
}

这里注意两点:

  1. 因为OperatorSubscribeOn是个OnSubscribe对象,所以在call参数中传入的subscriber就是我们在外面使用Observable.subscribe(a)传入的对象a

  2. 这里source对象指向的是调用subscribeOn之前的那个Observable序列。

明确了这两点,我们就很好的知道了subscribeOn是如何工作,产生神奇的效果了。
其实最最主要的就是一行函数

source.unsafeSubscribe(s);

并且要注意它所在的位置,是在worker的call里面,说白了,就是把source.subscribe这一行调用放在指定的线程里,那么总结起来的结论就是:

subscribeOn的调用,改变了调用前序列所运行的线程。

observeOn

同样看下OperatorObserveOn这个类的主要代码:

public final class OperatorObserveOn<T> implements Operator<T, T> {

    private final Scheduler scheduler;
    private final boolean delayError;

    /**
     * @param scheduler the scheduler to use
     * @param delayError delay errors until all normal events are emitted in the other thread?
     */
    public OperatorObserveOn(Scheduler scheduler, boolean delayError) {
        this.scheduler = scheduler;
        this.delayError = delayError;
    }

    @Override
    public Subscriber<? super T> call(Subscriber<? super T> child) {
        ....
        ObserveOnSubscriber<T> parent = new ObserveOnSubscriber<T>(scheduler, child, delayError);
        parent.init();
        return parent;
    }

    /** Observe through individual queue per observer. */
    private static final class ObserveOnSubscriber<T> extends Subscriber<T> implements Action0 {
        final Subscriber<? super T> child;
        final Scheduler.Worker recursiveScheduler;
        final NotificationLite<T> on;
        final boolean delayError;
        final Queue<Object> queue;
        
        // the status of the current stream
        volatile boolean finished;

        final AtomicLong requested = new AtomicLong();
        
        final AtomicLong counter = new AtomicLong();
        
        /** 
         * The single exception if not null, should be written before setting finished (release) and read after
         * reading finished (acquire).
         */
        Throwable error;

        // do NOT pass the Subscriber through to couple the subscription chain ... unsubscribing on the parent should
        // not prevent anything downstream from consuming, which will happen if the Subscription is chained
        public ObserveOnSubscriber(Scheduler scheduler, Subscriber<? super T> child, boolean delayError) {
            this.child = child;
            this.recursiveScheduler = scheduler.createWorker();
            this.delayError = delayError;
            this.on = NotificationLite.instance();
            if (UnsafeAccess.isUnsafeAvailable()) {
                queue = new SpscArrayQueue<Object>(RxRingBuffer.SIZE);
            } else {
                queue = new SpscAtomicArrayQueue<Object>(RxRingBuffer.SIZE);
            }
        }
        
        void init() {
            // don't want this code in the constructor because `this` can escape through the 
            // setProducer call
            Subscriber<? super T> localChild = child;
            
            localChild.setProducer(new Producer() {

                @Override
                public void request(long n) {
                    if (n > 0L) {
                        BackpressureUtils.getAndAddRequest(requested, n);
                        schedule();
                    }
                }

            });
            localChild.add(recursiveScheduler);
            localChild.add(this);
        }

        @Override
        public void onStart() {
            // signal that this is an async operator capable of receiving this many
            request(RxRingBuffer.SIZE);
        }

        @Override
        public void onNext(final T t) {
            if (isUnsubscribed() || finished) {
                return;
            }
            if (!queue.offer(on.next(t))) {
                onError(new MissingBackpressureException());
                return;
            }
            schedule();
        }

        @Override
        public void onCompleted() {
            if (isUnsubscribed() || finished) {
                return;
            }
            finished = true;
            schedule();
        }

        @Override
        public void onError(final Throwable e) {
            if (isUnsubscribed() || finished) {
                RxJavaPlugins.getInstance().getErrorHandler().handleError(e);
                return;
            }
            error = e;
            finished = true;
            schedule();
        }

        protected void schedule() {
            if (counter.getAndIncrement() == 0) {
                recursiveScheduler.schedule(this);
            }
        }

        // only execute this from schedule()
        @Override
        public void call() {
            long emitted = 0L;

            long missed = 1L;

            // these are accessed in a tight loop around atomics so
            // loading them into local variables avoids the mandatory re-reading
            // of the constant fields
            final Queue<Object> q = this.queue;
            final Subscriber<? super T> localChild = this.child;
            final NotificationLite<T> localOn = this.on;
            
            // requested and counter are not included to avoid JIT issues with register spilling
            // and their access is is amortized because they are part of the outer loop which runs
            // less frequently (usually after each RxRingBuffer.SIZE elements)
            
            for (;;) {
                long requestAmount = requested.get();
                long currentEmission = 0L;
                
                while (requestAmount != currentEmission) {
                    boolean done = finished;
                    Object v = q.poll();
                    boolean empty = v == null;
                    
                    if (checkTerminated(done, empty, localChild, q)) {
                        return;
                    }
                    
                    if (empty) {
                        break;
                    }
                    
                    localChild.onNext(localOn.getValue(v));

                    currentEmission++;
                    emitted++;
                }
                
                if (requestAmount == currentEmission) {
                    if (checkTerminated(finished, q.isEmpty(), localChild, q)) {
                        return;
                    }
                }
                
                if (currentEmission != 0L) {
                    BackpressureUtils.produced(requested, currentEmission);
                }
                
                missed = counter.addAndGet(-missed);
                if (missed == 0L) {
                    break;
                }
            }
            
            if (emitted != 0L) {
                request(emitted);
            }
        }
        
        boolean checkTerminated(boolean done, boolean isEmpty, Subscriber<? super T> a, Queue<Object> q) {
            if (a.isUnsubscribed()) {
                q.clear();
                return true;
            }
            
            if (done) {
                if (delayError) {
                    if (isEmpty) {
                        Throwable e = error;
                        try {
                            if (e != null) {
                                a.onError(e);
                            } else {
                                a.onCompleted();
                            }
                        } finally {
                            recursiveScheduler.unsubscribe();
                        }
                    }
                } else {
                    Throwable e = error;
                    if (e != null) {
                        q.clear();
                        try {
                            a.onError(e);
                        } finally {
                            recursiveScheduler.unsubscribe();
                        }
                        return true;
                    } else
                    if (isEmpty) {
                        try {
                            a.onCompleted();
                        } finally {
                            recursiveScheduler.unsubscribe();
                        }
                        return true;
                    }
                }
                    
            }
            
            return false;
        }
    }
}

这里的代码有点长,我们先注意到它是一个Operator,它没有对上层Observable做任何的控制或者包装。

既然是Operator,那么它的职责就是把一个Subscriber转换成另外一个Subscriber, 我们来关注下转换后的Subscriber对转换前的Subscriber做了些什么事。

首先它是一个ObserveOnSubscriber类, 既然是Subscriber那么肯定有onNextonComplete 和onError 看最主要的onNext

@Override
public void onNext(final T t) {
    if (isUnsubscribed() || finished) {
        return;
    }
    if (!queue.offer(on.next(t))) {
        onError(new MissingBackpressureException());
        return;
    }
    schedule();
}

好了,这里做了两件事,首先把结果缓存到一个队列里,然后调用schedule启动传入的worker

我们这里需要注意下:

在调用observeOn前的序列,把结果传入到onNext就是它的工作,它并不关心后续的流程,所以工作就到这里就结束了,剩下的交给ObserveOnSubscriber继续。

protected void schedule() {
    if (counter.getAndIncrement() == 0) {
        recursiveScheduler.schedule(this);
    }
}

recursiveScheduler 就是之前我们传入的Scheduler,我们一般会在observeOn传入AndroidScheluders.mainThread()对吧、

接下去,我们看下在scheduler中调用的call方法,这里只列出主要带代码

@Override
public void call() {
    ...
    final Subscriber<? super T> localChild = this.child;
    for (;;) {
        ...
        boolean done = finished;
        Object v = q.poll();
        boolean empty = v == null;
        
        if (checkTerminated(done, empty, localChild, q)) {
            return;
        }
        
        if (empty) {
            break;
        }
        
        localChild.onNext(localOn.getValue(v));

        ...
    }
    
    if (emitted != 0L) {
        request(emitted);
    }
}

OK,在Scheduler启动后, 我们在Observable.subscribe(a)传入的a就是这里的child, 我们看到,在call中终于调用了它的onNext方法,把真正的结果传了出去,但是在这里,我们是工作在observeOn的线程上的。

那么总结起来的结论就是:

  1. observeOn 对调用之前的序列默不关心,也不会要求之前的序列运行在指定的线程上

  2. observeOn 对之前的序列产生的结果先缓存起来,然后再在指定的线程上,推送给最终的subscriber

复杂情况

我们经常多次使用subscribeOn切换线程,那么以后是否可以组合observeOnsubscribeOn达到自由切换的目的呢?

组合是可以的,但是他们的执行顺序是有条件的,如果仔细分析的话,可以知道observeOn调用之后,再调用subscribeOn是无效的,原因是什么?

因为subscribeOn改变的是subscribe这句调用所在的线程,大多数情况,产生内容和消费内容是在同一线程的,所以改变了产生内容所在的线程,就改变了消费内容所在的线程。

经过上面的阐述,我们知道,observeOn的工作原理是把消费结果先缓存,再切换到新线程上让原始消费者消费,它和生产者是没有一点关系的,就算subscribeOn调用了,也只是改变observeOn这个消费者所在的线程,和OperatorObserveOn中存储的原始消费者一点关系都没有,它还是由observeOn控制。

总结

如果我们有一段这样的序列

Observable
.map                    // 操作1
.flatMap                // 操作2
.subscribeOn(io)
.map                    //操作3
.flatMap                //操作4
.observeOn(main)
.map                    //操作5
.flatMap                //操作6
.subscribeOn(io)        //!!特别注意
.subscribe(handleData)

假设这里我们是在主线程上调用这段代码,
那么

  1. 操作1操作2是在io线程上,因为之后subscribeOn切换了线程

  2. 操作3操作4也是在io线程上,因为在subscribeOn切换了线程之后,并没有发生改变。

  3. 操作5操作6是在main线程上,因为在他们之前的observeOn切换了线程。

  4. 特别注意那一段,对于操作5操作6是无效的

再简单点总结就是

  1. subscribeOn的调用切换之前的线程。

  2. observeOn的调用切换之后的线程。

  3. observeOn之后,不可再调用subscribeOn 切换线程

=========
续 特别感谢@扔物线给的额外的总结

  1. 下面提到的“操作”包括产生事件、用操作符操作事件以及最终的通过 subscriber 消费事件

  2. 只有第一subscribeOn() 起作用(所以多个 subscribeOn() 毛意义)

  3. 这个 subscribeOn() 控制从流程开始的第一个操作,直到遇到第一个 observeOn()

  4. observeOn() 可以使用多次,每个 observeOn() 将导致一次线程切换(),这次切换开始于这次 observeOn() 的下一个操作

  5. 不论是 subscribeOn() 还是 observeOn(),每次线程切换如果不受到下一个 observeOn() 的干预,线程将不再改变,不会自动切换到其他线程


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值