RxJava2.x 從原始碼分析原理

連續三屆村草發表於2019-02-19

RxJava 相信各位已經使用了很久,但大部分人在剛學習 RxJava 感嘆切換執行緒的方便,呼叫邏輯清晰的同時,並不知道其中的原理,主要是靠記住執行的順序。 隨著我們設計出的 RxJava流 越來越複雜,一些複雜的問題並不能靠著記住的執行順序就能解決。
下面,就通過最常用的操作符的原始碼來看看所謂的是什麼執行的。

首先我們用Single舉例,設計一個最基本的 RxJava 流,只有一個 Observable(ColdObservable)Obsever

Disposable disposable = Single.just("wtf")
              			.subscribe(it -> Log.i("subscribe", it));
複製程式碼

上游傳送一個"wtf" ,下游接受時將其列印出來。上游傳送端使用 Single.just 作為建立方法, 看一下 just() 方法裡做了什麼。

    public static <T> Single<T> just(final T item) {
        ObjectHelper.requireNonNull(item, "value is null");
        return RxJavaPlugins.onAssembly(new SingleJust<T>(item));
    }
    
    public static <T> Single<T> onAssembly(@NonNull Single<T> source) {
    Function<? super Single, ? extends Single> f = onSingleAssembly;
    if (f != null) {
        return apply(f, source);
    }
    return source;
}
複製程式碼

其中 ObjectHelper.requireNonNull 只是空檢查。
RxJavaPlugins.onAssembly 方法,這個方法其實就是通過一個全域性的變數 onSingleAssembly 來對方法進行 Hook ,這一系列xxxAssembly全域性變數預設為空,所以實際上當我們沒有設定的時候其實 just 方法是直接返回了一個 新例項化的SingleJust物件。

再看看SingleJust內部:

public final class SingleJust<T> extends Single<T> {

    final T value;
    public SingleJust(T value) {
        this.value = value;
    }

    @Override
    protected void subscribeActual(SingleObserver<? super T> observer) {
        observer.onSubscribe(Disposables.disposed());
        observer.onSuccess(value);
    }

}

複製程式碼

例項化的時候只是將值儲存了下來,沒有其它操作。
下一步呼叫subscribe()來啟動這個流(ColdObservable),然後看看subscribe中做了什麼:

    public final void subscribe(SingleObserver<? super T> subscriber) {
        ObjectHelper.requireNonNull(subscriber, "subscriber is null");
        subscriber = RxJavaPlugins.onSubscribe(this, subscriber);
        ObjectHelper.requireNonNull(subscriber, "subscriber returned by the RxJavaPlugins hook is null");

        try {
 	         //核心邏輯
            subscribeActual(subscriber);
        } catch (NullPointerException ex) {
            throw ex;
        } catch (Throwable ex) {
            Exceptions.throwIfFatal(ex);
            NullPointerException npe = new NullPointerException("subscribeActual failed");
            npe.initCause(ex);
            throw npe;
        }
    }
複製程式碼

同樣 RxJavaPlugins.onSubscribe 預設沒有作用,實際的核心邏輯是呼叫了subscribeActual(SingleObserver)
對於我們上面設計的流,則是呼叫了 SingleJust 中的 subscribeActual(SingleObserver)

回顧上面 SingleJustsubscribeActual(SingleObserver) 的實現:

        observer.onSubscribe(Disposables.disposed());
        observer.onSuccess(value);
複製程式碼

得到兩個資訊

  • 首先呼叫下游觀察者 SingleObserverOnSubscribe 方法並傳遞用於取消操作的 Disposable
  • 呼叫OnSuccess 方法並傳遞之前儲存下來的 value

Map 操作符

現在我們加入一個常用且重要的Map操作到流中

Disposable disposable = Single.just("wtf")
				 .map(it-> 0)
                 .subscribe(it -> Log.i("subscribe", String.of(it)));
複製程式碼

上面這個流包括了三種典型的操作 建立Creation 操作符Transformation和 訂閱Subscribe

依然先檢查map() 方法,可以看到其中例項化了一個SingleMap

    public final <R> Single<R> map(Function<? super T, ? extends R> mapper) {
        ObjectHelper.requireNonNull(mapper, "mapper is null");
        return RxJavaPlugins.onAssembly(new SingleMap<T, R>(this, mapper));
    }
複製程式碼

再看看 SingleMap

public final class SingleMap<T, R> extends Single<R> {
    final SingleSource<? extends T> source;
    final Function<? super T, ? extends R> mapper;

    public SingleMap(SingleSource<? extends T> source, Function<? super T, ? extends R> mapper) {
        this.source = source;
        this.mapper = mapper;
    }

    @Override
    protected void subscribeActual(final SingleObserver<? super R> t) {
        source.subscribe(new MapSingleObserver<T, R>(t, mapper));
    }

    static final class MapSingleObserver<T, R> implements SingleObserver<T> {

        final SingleObserver<? super R> t;
        final Function<? super T, ? extends R> mapper;

        MapSingleObserver(SingleObserver<? super R> t, Function<? super T, ? extends R> mapper) {
            this.t = t;
            this.mapper = mapper;
        }

        @Override
        public void onSubscribe(Disposable d) {
            t.onSubscribe(d);
        }

        @Override
        public void onSuccess(T value) {
            R v;
            try {
                v = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper function returned a null value.");
            } catch (Throwable e) {
                Exceptions.throwIfFatal(e);
                onError(e);
                return;
            }

            t.onSuccess(v);
        }

        @Override
        public void onError(Throwable e) {
            t.onError(e);
        }
    }
}
複製程式碼

類中資訊稍微複雜一些:

  1. 首先我們關注在SingleMap例項化的時候也是隻做了儲存資料的操作,而沒有實際邏輯:將流的上游儲存為 source 將資料轉換的方法儲存為 mapper
  2. 第二步我們知道下游觀察者 SingleObserver 會呼叫核心邏輯 subscribeActual方法來啟動流
  3. 在這裡的subscribeActual方法中可以看到幾個重要的資訊
    • MapSingleObserver是一個觀察者
    • MapSingleObserver 儲存了下游的觀察者 SingleObserver 以及 mapper
    • 上游 sourceMapSingleObserver 訂閱

由此可以看出在SingleMap被下游觀察者訂閱了之後,例項化了一個新的觀察者MapSingleObserver並儲存下游觀察者SingleObserver的資訊,再去訂閱上游SingleJust
這種模式建立了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能的設計模式稱為裝飾者模式

總結上面的執行順序:

  1. Rx流的最後一步呼叫 subscribe啟動流(ColdObservable)
  2. 首先執行SingleMap中的subscribeActual方法,其中包括生成新的MapSingleObserver並訂閱 SingleJust
  3. 執行SingleJust中的subscribeActual:呼叫下游MapSingleObserveronSubscribe onSuccess方法
  4. MapSingleObserver中的onSubsribe``onSuccess方法也很簡單,分別呼叫下游 ObserveronSubsribe``onSuccess(異常時 onError)方法

observeOn 操作符

Rxjava首先被大家津津樂道之處是可以方便的切換執行緒,避免Callback Hell,現在來看看執行緒切換操作符。
我們加入執行緒切換操作符 observeOn

Disposable disposable = Single.just("wtf")
				 .map(it-> 0)
				 .observeOn(Schedulers.io())
                 .subscribe(it -> Log.i("subscribe", String.of(it)));
複製程式碼

同樣的,在 observeOn方法中例項化了一個SingleObserveOn

    public final Single<T> observeOn(final Scheduler scheduler) {
        ObjectHelper.requireNonNull(scheduler, "scheduler is null");
        return RxJavaPlugins.onAssembly(new SingleObserveOn<T>(this, scheduler));
    }
複製程式碼

繼續看SingleObserveOn類中資訊

public final class SingleObserveOn<T> extends Single<T> {

    final SingleSource<T> source;
    final Scheduler scheduler;

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

    @Override
    protected void subscribeActual(final SingleObserver<? super T> s) {
        source.subscribe(new ObserveOnSingleObserver<T>(s, scheduler));
    }

    static final class ObserveOnSingleObserver<T> extends AtomicReference<Disposable>
    implements SingleObserver<T>, Disposable, Runnable {
        private static final long serialVersionUID = 3528003840217436037L;

        final SingleObserver<? super T> actual;
        final Scheduler scheduler;

        T value;
        Throwable error;

        ObserveOnSingleObserver(SingleObserver<? super T> actual, Scheduler scheduler) {
            this.actual = actual;
            this.scheduler = scheduler;
        }

        @Override
        public void onSubscribe(Disposable d) {
            if (DisposableHelper.setOnce(this, d)) {
                actual.onSubscribe(this);
            }
        }

        @Override
        public void onSuccess(T value) {
            this.value = value;
            Disposable d = scheduler.scheduleDirect(this);
            DisposableHelper.replace(this, d);
        }

        @Override
        public void onError(Throwable e) {
            this.error = e;
            Disposable d = scheduler.scheduleDirect(this);
            DisposableHelper.replace(this, d);
        }

        @Override
        public void run() {
            Throwable ex = error;
            if (ex != null) {
                actual.onError(ex);
            } else {
                actual.onSuccess(value);
            }
        }

        @Override
        public void dispose() {
            DisposableHelper.dispose(this);
        }

        @Override
        public boolean isDisposed() {
            return DisposableHelper.isDisposed(get());
        }
    }
}


複製程式碼

類似的

  • 建構函式中儲存了上游和執行緒切換的資訊
  • subscribeActual 例項化了一個新的觀察者ObserveOnSingleObserver

不同的

  • ObserveOnSingleObserver 還繼承了AtomicReference<Disposable>、實現了Disposable``Runnable介面
  • onSuccess``onError中都沒有直接呼叫下游的onSuccess``onError方法,而是呼叫了Disposable d = scheduler.scheduleDirect(this);來執行run方法中的邏輯,而run方法中的邏輯則是呼叫下游的onSuccess``onError方法

檢視schedulerDirect內部資訊

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

建立了一個對應執行緒的Worker和一個可用於取消的DisposeTask並執行,對於IoScheduler則是建立了EventLoopWorker,再看看EventLoopWorker中的資訊。

    @Override
    public Worker createWorker() {
        return new EventLoopWorker(pool.get());
    }
複製程式碼
    static final class EventLoopWorker extends Scheduler.Worker {
        private final CompositeDisposable tasks;
        private final CachedWorkerPool pool;
        private final ThreadWorker threadWorker;

        final AtomicBoolean once = new AtomicBoolean();

        EventLoopWorker(CachedWorkerPool pool) {
            this.pool = pool;
            this.tasks = new CompositeDisposable();
            this.threadWorker = pool.get();
        }

        @Override
        public void dispose() {
            if (once.compareAndSet(false, true)) {
                tasks.dispose();

                // releasing the pool should be the last action
                pool.release(threadWorker);
            }
        }

        @Override
        public boolean isDisposed() {
            return once.get();
        }

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

EventLoopWorker中則是維護了一套包含相應的執行緒池、可取消的CompositeDisposable、以及用於執行RunableThreadWorker。總的來說就是一套可以在相應執行緒執行且可取消的類和邏輯。

  • 上面則解釋了為什麼observeOn可以切換下游的執行緒(onSuccess``onError)
  • 同樣解釋了為什麼不會改變onSubsribe的呼叫執行緒,因為可以看到onSubscribe方法中直接呼叫了下游的onSucsribe,並沒有受到執行緒切換的影響。

SubscribeOn

現在設計兩個Rx流

Disposable disposable = Single.just("wtf")
				 .doOnSubsribe(it-> Log.i("doOnSubsribe", 0)
				 .doOnSubsribe(it-> Log.i("doOnSubsribe", 1)
				 .doOnSubsribe(it-> Log.i("doOnSubsribe", 2)
				 .doOnSubsribe(it-> Log.i("doOnSubsribe", 3)
                 .subscribe(it -> Log.i("subscribe", 4);
複製程式碼
Disposable disposable2 = Single.just("wtf")
				 .doOnSubsribe(it-> Log.i("doOnSubsribe", 0)
				 .doOnSubsribe(it-> Log.i("doOnSubsribe", 1)
				 .subscribeOn(Schedulers.io())
				 .doOnSubsribe(it-> Log.i("doOnSubsribe", 2)
				 .doOnSubsribe(it-> Log.i("doOnSubsribe", 3)
                 .subscribe(it -> Log.i("subscribe", 4);
複製程式碼

你可能已經知道並記住了兩個流的列印的順序分別是 01234``23014,但是為什麼doOnSubsribe方法和RxJava1中呼叫順序完全不一樣,為什麼通過subscribeOn切換執行緒會影響執行順序?

先找到 SingleSubscribeOn

public final class SingleSubscribeOn<T> extends Single<T> {
    final SingleSource<? extends T> source;
    final Scheduler scheduler;
    
    public SingleSubscribeOn(SingleSource<? extends T> source, Scheduler scheduler) {
        this.source = source;
        this.scheduler = scheduler;
    }

    @Override
    protected void subscribeActual(final SingleObserver<? super T> s) {
        final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s, source);
        //直接呼叫下游 onSubscribe
        s.onSubscribe(parent);
        //再執行訂閱上游的方法
        Disposable f = scheduler.scheduleDirect(parent);
        parent.task.replace(f);
    }

    static final class SubscribeOnObserver<T>
    extends AtomicReference<Disposable>
    implements SingleObserver<T>, Disposable, Runnable {

        private static final long serialVersionUID = 7000911171163930287L;
        final SingleObserver<? super T> actual;
        final SequentialDisposable task;
        final SingleSource<? extends T> source;
        
        SubscribeOnObserver(SingleObserver<? super T> actual, SingleSource<? extends T> source) {
            this.actual = actual;
            this.source = source;
            this.task = new SequentialDisposable();
        }

        @Override
        public void onSubscribe(Disposable d) {
        	  //沒有繼續呼叫下游的 onSubscribe 方法
            DisposableHelper.setOnce(this, d);
        }

        @Override
        public void onSuccess(T value) {
            actual.onSuccess(value);
        }

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

        @Override
        public void dispose() {
            DisposableHelper.dispose(this);
            task.dispose();
        }

        @Override
        public boolean isDisposed() {
            return DisposableHelper.isDisposed(get());
        }

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

}
複製程式碼

同樣的直接看subscribeActual方法及onSubscribe方法,和之前的操作符的邏輯區別很大:

  • SubscribeOnObserver同樣還繼承了AtomicReference<Disposable>,實現了Disposable``Runnable介面
  • 並沒有直接呼叫subscribe訂閱上游,而是執行了其它操作符在 onSubscribe中訂閱下游的操作
  • 然後再結合Disposable f = scheduler.scheduleDirect(parent);run方法可以知道在新的執行緒中執行了訂閱上游的操作 source.subscribe(this);
  • onSubsribe中並沒有再繼續呼叫下游的 onSubsribe

綜合起來可以知道,本來應該在整個流從下至上訂閱完成後按照從上至下的順序執行 onSubscribe的流,在使用subsribeOn操作符的後,在訂閱的時(執行subscribeActual),就開始執行下游的onSubscribe且在當前執行緒!然後才在指定的io執行緒執行之下而上的操作,這也是為什麼subsribeOn影響的是上游的執行緒。

小結:

我認為實際上 Rx 使用了很多優秀的設計將我們各種常用的操作進行了封裝,讓我們自由組合使用,其本身並沒有用什麼黑科技。例如切換執行緒本質上則是幫我們啟用了一個新的執行緒並把接下來的程式碼放進去執行。
當然,其中還有很多更深入的內容需要我們繼續發現和學習。

相關文章