RxJava 是如何實現執行緒切換的(下)

IAM四十二發表於2018-06-04

前言

通過前一篇的RxJava2 是如何實現執行緒切換的 (上)我們已經知道了在RxJava中,subscribeOn 將上游執行緒切換到指定的子執行緒是如何實現的。這裡就接著來看,observeOn 是如何將下游執行緒切換到指定執行緒的。

RxJava - subscribeOn

這裡可以通過UML圖簡單回顧一下subscribeOn的原理。

RxJava 是如何實現執行緒切換的(下)

通過 subscribeOn 我們完成了以下操作:

  • 建立了一個 ObservableSubscribeOn 物件,本質上來說他就是一個Observable,他同時實現了 AbstractObservableWithUpstream(HasUpstreamObservableSource )這樣一個介面,是他變了一個擁有上游的Observeable。
  • 在 ObservableSubscribeOn 的 subscribeActual 方法中
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
複製程式碼

將真正的 subscribe 操作安置在了SubscribeTask這樣個一個Runnable當中,這個 Runnable 將由scheduler 這個排程器負責啟動,因此就把上游操作放到了 scheduler 所在的執行緒中。

  • Schedulers.newThread()或者Schedulers.io() 都是通過工廠方法的模式建立了某種指定型別的執行緒, 當這個特定的執行緒執行是,就是執行真實的 subscribe 方法,這樣就把上游操作放到了一個特定的執行緒中去執行。

RxJava - observeOn

簡單回顧完 subscribeOn 之後,我們就來看看 observeOn 是如何工作的。

其實,瞭解 subscribeOn 的原理之後,再來看 observeOn 就簡單多了,類的命名及實現思路都有很多相似之處,可以對照著理解

ObserveOn

RxJava的程式碼寫的非常巧妙,可以說是百讀不厭,可以學習的地方特別多。為了避免陷入只見樹木不見森林的噩夢,我們就帶著以下問題去探索 observeOn 的奧祕。

  1. 在 Android 中執行緒間傳遞訊息會使用 Handler,這裡是否使用?又是如何使用的?
  2. AndroidSchedulers.mainThread() 做了什麼 ?
  3. 下游任務是如何保證被分配到指定執行緒的。

示例


    private void multiThread() {
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                e.onNext("This msg from work thread :" + Thread.currentThread().getName());
                sb.append("\nsubscribe: currentThreadName==" + Thread.currentThread().getName());
            }
        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<String>() {
                    @Override
                    public void accept(String s) throws Exception {
                        Log.e(TAG, "accept: s= " + s);
                    }
                });
    }

複製程式碼

我們還是以這段程式碼為例,來看看 observeOn 的工作原理。這裡通過observeOn(AndroidSchedulers.mainThread())將下游執行緒切換到了我們非常熟悉的 Android UI 執行緒。這樣就可以確保我們在下游所有的操作都是在 UI 執行緒中完成。這裡和討論 subscribeOn 一樣,我們就從這句程式碼出發,看看這背後到底發生了什麼。

有了上一篇的經驗,我們知道 AndroidSchedulers.mainThread() 一定去建立了某種型別的排程器,為了方便後面的敘述,這一次我們先從排程器的建立說起,後面再看 observeOn() 的具體實現。

需要注意的是 AndroidSchedulers 並不是 RxJava 的一部分,是為了在 Android 中方便的使用 RxJava 而專門設計的一個排程器實現,原始碼RxAndroid 設計非常巧妙;使用前記得在gradle檔案中配置依賴。

AndroidSchedulers.mainThread()

下面就來看看 AndroidSchedulers.mainThread() 這個我們非常熟悉的 Scheduler 是如何建立的。

public final class AndroidSchedulers {

    private static final class MainHolder {

        static final Scheduler DEFAULT = new HandlerScheduler(new Handler(Looper.getMainLooper()));
    }

    private static final Scheduler MAIN_THREAD = RxAndroidPlugins.initMainThreadScheduler(
            new Callable<Scheduler>() {
                @Override public Scheduler call() throws Exception {
                    return MainHolder.DEFAULT;
                }
            });

    public static Scheduler mainThread() {
        return RxAndroidPlugins.onMainThreadScheduler(MAIN_THREAD);
    }
}
複製程式碼

這裡我們可以認為,當呼叫AndroidSchedulers.mainThread() 時,返回了一個HandlerScheduler 的例項,而這個例項使用到了我們非常熟悉的 Handler。那麼重點就來到HandlerScheduler 了。

final class HandlerScheduler extends Scheduler {
    private final Handler handler;

    HandlerScheduler(Handler handler) {
        this.handler = handler;
    }

    @Override
    public Disposable scheduleDirect(Runnable run, long delay, TimeUnit unit) {
        if (run == null) throw new NullPointerException("run == null");
        if (unit == null) throw new NullPointerException("unit == null");

        run = RxJavaPlugins.onSchedule(run);
        ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);
        handler.postDelayed(scheduled, Math.max(0L, unit.toMillis(delay)));
        return scheduled;
    }

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

    private static final class HandlerWorker extends Worker {
        private final Handler handler;

        private volatile boolean disposed;

        HandlerWorker(Handler handler) {
            this.handler = handler;
        }

        @Override
        public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
            if (run == null) throw new NullPointerException("run == null");
            if (unit == null) throw new NullPointerException("unit == null");

            if (disposed) {
                return Disposables.disposed();
            }

            run = RxJavaPlugins.onSchedule(run);

            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, Math.max(0L, 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;
        }

        @Override
        public void dispose() {
            disposed = true;
            handler.removeCallbacksAndMessages(this /* token */);
        }

        @Override
        public boolean isDisposed() {
            return disposed;
        }
    }

    private static final class ScheduledRunnable implements Runnable, Disposable {
        private final Handler handler;
        private final Runnable delegate;

        private volatile boolean disposed;

        ScheduledRunnable(Handler handler, Runnable delegate) {
            this.handler = handler;
            this.delegate = delegate;
        }

        @Override
        public void run() {
            try {
                delegate.run();
            } catch (Throwable t) {
                IllegalStateException ie =
                    new IllegalStateException("Fatal Exception thrown on Scheduler.", t);
                RxJavaPlugins.onError(ie);
                Thread thread = Thread.currentThread();
                thread.getUncaughtExceptionHandler().uncaughtException(thread, ie);
            }
        }

        @Override
        public void dispose() {
            disposed = true;
            handler.removeCallbacks(this);
        }

        @Override
        public boolean isDisposed() {
            return disposed;
        }
    }
}
複製程式碼

這個類雖然很簡單,但是設計非常巧妙。

  • 首先 HandlerScheduler 是一個 Scheduler ,通過建構函式他獲取到了主執行緒所在的 Handler例項。而在他的 createWorker() 方法中,他又通過這個 Handler 例項建立了一個HandlerWorker 的例項,這個HandlerWorker 本質上就是一個 Worker。在他的 schedule 方法中,建立了一個 ScheduleRunnable 物件,並會把這個Runnable物件通過 handler 的 sendMessageDelayed 方法傳送出去,而我們知道這個 Handler 是主執行緒,這樣在下游中,就把任務從某個子執行緒轉移到了UI執行緒。

  • ScheduleRunnable 不但實現了 Runnable ,而且實現了我們看到過無數次的 Disposable 。

        @Override
        public void run() {
            try {
                delegate.run();
            } catch (Throwable t) {

            }
        }

        @Override
        public void dispose() {
            disposed = true;
            handler.removeCallbacks(this);
        }
複製程式碼

這樣,正確情況下 run 方法會正常執行執行緒中的任務,而一旦 disposable 物件執行了dispose()方法,那麼 handler.removeCallbacks(this),就可確保在 handler 的 dispatchMessage 方法中,不會在執行任何操作,從而達到了 dispose 的效果。

observeOn

下面就來看看 Observable 中的 observeOn 方法

Observable.java --- observeOn


    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));
    }
複製程式碼

這個方法的實現和 subscribeOn 的實現非常相似,多了兩個引數 delayError 和 buffersize 。 buffersize 可以認為是RxJava內部的一個靜態變數,預設情況下他的值是128。通過我們之前的經驗,這裡可以把 observeOn 的過程簡化如下:

new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize)
複製程式碼

也就是說 observeOn 這個操作符給我們返回了一個 ObservableObserveOn 物件。很容易想到他也是一個 Observeable。那麼我們就去看看這個 ObservableObserveOn 到底是什麼?我們最關心的 subscribeActual 方法他又是怎樣實現的。

ObservableObserveOn

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

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

複製程式碼

和 ObservableSubscribeOn 一樣,他也繼承了 AbstractObservableWithUpstream ,這樣他也是一個擁有上游的 Observeable,他的建構函式很簡單,沒什麼可以說。這裡我們重點關注一下 subscribeActual 方法的實現。這裡我們的使用的Scheduler 例項是 AndroidSchedulers.mainThread(),因此就按 else的邏輯分析。


            Scheduler.Worker w = scheduler.createWorker();

            source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
複製程式碼

通過 scheduler.createWorker() 建立了 Worker 這個物件。這裡結合之前對 AndroidSchedulers.mainThread() 的分析,此處的 worker 物件是就是一個持有主執行緒 handler 引用的 Worker。

接著用這個worker又建立了一個ObserveOnObserver物件。看看這個類的實現。

    static final class ObserveOnObserver<T> extends BasicIntQueueDisposable<T>
    implements Observer<T>, Runnable { ....}
複製程式碼

這個類功能非常強大,首先是一個 Observer ,同時也是一個Runnable,並且還繼承了 BasicIntQueueDisposable(保證原子性、擁有操作佇列功能和 Disposable功能)。

source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
複製程式碼

我們關注一下這行程式碼,根據之前的說法這裡的 source 是其父類(AbstractObservableWithUpstream)中的成員變數,也就是說是上游,那麼當前ObservableObserveOn 的上游是誰呢? 就是我們上一篇所說的 ObservableSubscribeOn 。

因此,當這裡開始執行訂閱方法 subscribe() 後,將以如下順序響應:

Observable.subscribe--->Observable.subscribeActual---> ObservableObserveOn.subscribeActual---> ObservableSubscribeOn.subscribeActual--->ObservableCreate.subscribeActual

這些方法的引數均為 observer,通過層層回撥,最後的 subscribeActual(Observer<? super T> observer) 執行時,這個 observer 持有之前幾個 observer 的引用。

我們再看一下 ObservableCreate.subscribeActual

    @Override
    protected void subscribeActual(Observer<? super T> observer) {
        CreateEmitter<T> parent = new CreateEmitter<T>(observer);
        observer.onSubscribe(parent);

        try {
            source.subscribe(parent);
        } catch (Throwable ex) {
            Exceptions.throwIfFatal(ex);
            parent.onError(ex);
        }
    }
複製程式碼

可以看到,這裡首先會觸發 observer.onSubscribe ,我們再看一下 ObservableSubscribeOn.subscribeActual

    @Override
    public void subscribeActual(final Observer<? super T> s) {
        final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);

        s.onSubscribe(parent);

        parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
    }
複製程式碼

好了,這樣我們又回到了原點:

source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
複製程式碼

回到了最初的 Observer:ObserveOnObserver

這個 ObserveOnObserver 持有我們一開始建立的observer,也就是一個Consumer物件。

下面就來看看這個 ObserveOnObserver

  • 建構函式
        ObserveOnObserver(Observer<? super T> actual, Scheduler.Worker worker, boolean delayError, int bufferSize) {
            this.actual = actual;
            this.worker = worker;
            this.delayError = delayError;
            this.bufferSize = bufferSize;
        }
複製程式碼

這裡指的注意的一點 ,actual 其實就是observer

  • onSubscribe
@Override
        public void onSubscribe(Disposable s) {
            if (DisposableHelper.validate(this.s, s)) {
                this.s = s;
				// 現階段,我們用到的Disposable 都是單個的,暫時不討論其
				//為QueueDisposable的情況

                queue = new SpscLinkedArrayQueue<T>(bufferSize);

                actual.onSubscribe(this);
            }
        }
複製程式碼

ObservableCreate.subscribeActual 中我們知道,當執行subscribe 方法後,首先會執行 observer的 onSubscribe 方法。這裡的實現非常簡單,就是建立了一個queue,並觸發了這個 observer 自己的 onSubscribe 方法。

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

            if (sourceMode != QueueDisposable.ASYNC) {
                queue.offer(t);
            }
            schedule();
        }
複製程式碼

在 onNext 中會執行 scheule() 方法。

        void schedule() {
            if (getAndIncrement() == 0) {
                worker.schedule(this);
            }
        }
複製程式碼

這個地方就有意思了,前面說過這裡的 worker 是一個持有主執行緒handler 的Worker物件,當他的 schedule 執行時,就會把特定的執行緒任務通過Handler.postDelay 方法轉移到主線中去執行

那麼這裡的this 又是什麼呢?前面我們說過,ObserveOnObserver 這個類功能非常強大,他是一個Runnable,那麼這裡就是執行他自己的run方法嘍,我們趕緊看看。

        @Override
        public void run() {
            if (outputFused) {
                drainFused();
            } else {
                drainNormal();
            }
        }
複製程式碼

這裡有一個引數 outputFused 他預設是false,至於他什麼時候為true,不作為這裡討論的重點。

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) {
                        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;
                }
            }
        }
複製程式碼

這裡大概就是通過一個死迴圈,不斷從 onSubscribe 方法中建立的佇列中取出事件,執行observer 的 onNext方法。而當為例為空時,就會執行worker.dispose 取消整個事件流,同時從Handler中移除所有訊息。

最後在看一眼 onComplete ,onError 和整個類似

        @Override
        public void onComplete() {
            if (done) {
                return;
            }
            done = true;
            schedule();
        }
複製程式碼

可以看到這裡的處理也很簡單,done 設定為 true .這樣最後便完成了下游事件的執行。

最後

好了,由於一些無以訴說的原因,經歷了很久終於把 RxJava 執行緒切換的下篇給完成了。

相關文章