詳解 RxJava2 的執行緒切換原理

QuincySx發表於2019-03-24

轉載請標明地址 QuincySx:[www.jianshu.com/p/a9ebf730c… ]


讀了這篇文章你將會收穫什麼

  • RxJava2 基本的執行流程(並不會詳述)
  • RxJava2 執行緒切換原理
  • 為什麼 subscribeOn() 只有第一次切換有效
  • RxAndroid 簡單分析

PS:建議您對 RxJava 有一些瞭解或使用經驗再看此文章,推薦結合原始碼品嚐 RxJava入門文章 [給 Android 開發者的 RxJava 詳解-扔物線(gank.io/post/560e15…)

然後貼一下本篇文章分析的示例程式碼

CompositeDisposable comDisposable = new CompositeDisposable();

protected void test() {
        Observable<String> observable = Observable
                .create(new ObservableOnSubscribe<String>() {
                    @Override
                    public void subscribe(@NonNull ObservableEmitter<String> emitter) throws
                            Exception {
                        emitter.onNext("hello");
                    }
                })
                .map(new Function<String, String>() {
                    @Override
                    public String apply(String s) throws Exception {
                        return s;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());

        observable.subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                comDisposable.add(d);
            }

            @Override
            public void onNext(String s) {
                Log.i(TAG, s);
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });
}
複製程式碼

RxJava2 基本的執行流程

根據上述原始碼分析出流程圖,這裡顏色相同的代表同一物件。根據流程圖看一遍原始碼基本流程就能理通

RxJava2 執行緒切換原理流程圖

RxJava2 執行緒切換原理

RxJava 切換執行緒怎麼用我就不多說了請參考我的另一篇文章 Android:隨筆——RxJava的執行緒切換

一、observeOn() 的執行緒切換原理

根據執行流程來看 observeOn() 執行後是得到 ObservableObserveOn 物件,那麼當 ObservableObserveOn 繫結監聽者的時候要執行 subscribe() 方法

public final void subscribe(Observer<? super T> observer) {
    ObjectHelper.requireNonNull(observer, "observer is null");
    try {
        observer = RxJavaPlugins.onSubscribe(this, observer);
        ObjectHelper.requireNonNull(observer, "Plugin returned null Observer");
        //呼叫 subscribeActual()
        subscribeActual(observer);
    } catch (NullPointerException e) { // NOPMD
        throw e;
    } catch (Throwable e) {
        ...
    }
}
複製程式碼

接下來我們看一下 subscribeActual() 方法

protected void subscribeActual(Observer<? super T> observer) {
    if (scheduler instanceof TrampolineScheduler) {
        source.subscribe(observer);
    } else {
        //scheduler 是傳進來的執行緒排程物件,如 Schedulers.io() 、AndroidSchedulers.mainThread() 等,這裡呼叫了 createWorker() 方法暫時看一下就好稍後分析 RxAndroid 會說明 
        Scheduler.Worker w = scheduler.createWorker();
        //我們看到他把 w 引數傳進去了
        source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
    }
}
複製程式碼

從上述程式碼我們可以看到 ObservableObserveOn 是被 ObserveOnObserver 監聽的,所以收到通知也是由 ObserveOnObserver 作出響應,接下來我們假設當 Rxjava 傳送 onNext 通知時會呼叫 ObserveOnObserver 的 onNext() 方法 ( PS:當然如果是 onComplete()、onError() 等也是一樣的邏輯 ),然後我們來看一看 ObserveOnObserver 的 onNext() 方法,

@Override
public void onNext(T t) {
    if (done) {
        return;
    }
    if (sourceMode != QueueDisposable.ASYNC) {
        queue.offer(t);
    }
    //切換執行緒
    schedule();
}

void schedule() {
    if (getAndIncrement() == 0) {
        //直接呼叫了 worker 的 schedule 方法,需要注意的是這裡他把自己傳了進去
        worker.schedule(this);
    }
}
複製程式碼

現在我先把把 schedule(Runnable run) 貼出來

public Disposable schedule(@NonNull Runnable run) {
    return schedule(run, 0L, TimeUnit.NANOSECONDS);
}
複製程式碼
  1. 我們看到這個他接收的引數是一個 Runnable,這是怎麼回事呢,我們看一下 ObserveOnObserver 物件,他不但實現了 Observer 介面並且也實現了 Runnable 介面
  2. 接下看,繼續呼叫 schedule( Runnable action, long delayTime, TimeUnit unit) 方法,但是這個方法是個抽象方法,這裡我們就假設這裡這個 worker 是 IO 執行緒,所以我直接貼 IoScheduler 的程式碼了
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);
}
複製程式碼

然後再貼一下 scheduleActual 的方法

public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
    Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
    //就是個 Runnable
    ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);
        
    if (parent != null) {
        if (!parent.add(sr)) {
            return sr;
        }
    }

    Future<?> f;
    try {
        //判斷延遲時間,然後使用執行緒池執行 Runnable
        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;
}
複製程式碼

這樣一來就會在相應的執行緒中執行 ObserveOnObserver 的 run 方法

public void run() {
    //這個地方具體的我還沒有搞明白,大概就是在這個方法裡呼叫 onNext() ,然後 observeOn() 操作符之後的監聽者的執行執行緒就變了
    if (outputFused) {
        drainFused();
    } else {
        drainNormal();
        }
    }
複製程式碼
二、subscribeOn() 的執行緒切換原理

PS:這個切換原理其實和 observeOn() 原理很像

跟 observeOn() 一樣,只不過這個操作的物件是 ObservableSubscribeOn, 這個物件也是同樣的程式碼邏輯,執行 subscribe() 方法,然後呼叫 subscribeActual() 方法,所以就直接貼 subscribeActual() 的程式碼

public void subscribeActual(final Observer<? super T> s) {
    //建立與之繫結的 SubscribeOnObserver
    final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
    s.onSubscribe(parent);
    //1. 建立 SubscribeTask 實際上就是個 Runnable
    //2. 然後呼叫 scheduler.scheduleDirect 方法
    parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
複製程式碼

我們看一下 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);
    //一個 Runnable 具體作用沒分析
    DisposeTask task = new DisposeTask(decoratedRun, w);
    //這個程式碼看著熟悉嗎  沒錯上面 observeOn 提到過,知道它是執行 Runnable 我們就直接看 Runnable 裡面的 run() 了
    w.schedule(task, delay, unit);
    return task;
}
複製程式碼

我們看一下 DisposeTask 的 run()

public void run() {
    runner = Thread.currentThread();
    try {
        decoratedRun.run();
    } finally {
        dispose();
        runner = null;
    }
}
複製程式碼

調來調去我們又回到了 SubscribeTask 的 run()

public void run() {
    source.subscribe(parent);
}
複製程式碼

這個地方的執行執行緒已經被切換了,他又開始往上一層層的去訂閱,所以 create(new ObservableOnSubscribe(){})這個匿名實現介面執行 subscribe 的執行緒執行環境都被改變了,再去呼叫 onNext() 等方法執行緒環境也是被改變的

為什麼 subscribeOn() 只有第一次切換有效

寫到這裡我們這個問題也就能回答了 因為 RxJava 最終能影響 ObservableOnSubscribe 這個匿名實現介面的執行環境的只能是最後一次執行的 subscribeOn() ,又因為 RxJava 訂閱的時候是從下往上訂閱,所以從上往下第一個 subscribeOn() 就是最後執行的,這就造成了寫多個 subscribeOn() 並沒有什麼亂用的現象。


分析一下 RxAndroid

RxAndroid 原始碼
其實 RxAndroid 裡面並沒有什麼複雜的程式碼,他其實只是提供一個能切換到 Android 主執行緒執行緒排程器。

其實它的原理和 RxJava 自帶的那些執行緒排程器一樣,如果你想了解 RxJava 的 IO 執行緒池,什麼的可以自己看一看,我這裡分析 RxAndroid 主要有以下幾點原因

  1. 弄清楚 RxAndroid 這個庫的具體作用
  2. 弄清楚他是怎麼就能把執行緒切換到主執行緒(他是怎麼提供的主執行緒環境)
  3. 弄清楚執行緒排程器的執行原理
  4. 最重要的是它相對於 RxJava 自帶的那些排程器,他比較簡單容易分析

正文開始

首先我們找一下入口 AndroidSchedulers.mainThread() 這個地方應該是就是入口了,我們看一下 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);
    }

    public static Scheduler from(Looper looper) {
        if (looper == null) throw new NullPointerException("looper == null");
        return new HandlerScheduler(new Handler(looper));
    }
複製程式碼

這個應該不用我多說大家都能看明白,看到這裡我們基本上明白了 RxAndroid 就是通過 Handler 來拿到主執行緒的

我們拿 subscribeOn() 中的一些流程來說

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

首先我們看到呼叫了 createWorker() 這是個抽象方法我們找到具體實現類 HandlerScheduler

public Worker createWorker() {
    return new HandlerWorker(handler);
}
複製程式碼

單純的建立一個 Worker 並把主執行緒的 Handler 傳進去,然後呼叫 Worker 的 schedule() 方法

public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
    /**忽略一些程式碼**/
    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, unit.toMillis(delay));

    if (disposed) {
          handler.removeCallbacks(scheduled);
          return Disposables.disposed();
    }
    return scheduled;
}
複製程式碼

到這裡看明白 RxJava 如何通過 RxAndroid 來切換到主執行緒執行,其實 RxAndroid 的核心就是 Handler


總結

本篇參考 RxJava 2.1.12 與 RxAndroid:2.0.2 原始碼 不得不說 Handler 在安卓中的地位真的是很牛逼 見解不到的地方歡迎大家指出

相關文章