系列文章:
本文 csdn 地址:友好 RxJava2.x 原始碼解析(二)執行緒切換
本文基於 RxJava 2.1.3
前言
本文基於讀者會使用 RxJava 2.x 而講解,基本原理不涉及,示例只純粹為示例而示例。示例程式碼
示例原始碼:Observable
.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
Log.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) {
Log.e("TAG", "onSubscribe(): 所線上程為 " + Thread.currentThread().getName());
}
@Override
public void onNext(String s) {
Log.e("TAG", "onNext(): 所線上程為 " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
Log.e("TAG", "onComplete(): 所線上程為 " + Thread.currentThread().getName());
}
});
複製程式碼
輸出結果:
E/TAG: onSubscribe(): 所線上程為 main
E/TAG: subscribe(): 所線上程為 RxCachedThreadScheduler-1
E/TAG: onNext(): 所線上程為 main
E/TAG: onComplete(): 所線上程為 main
複製程式碼
原始碼解析
我們可以發現,除了 Observable 的 subscribe(ObservableEmitter)
方法執行在 io 執行緒,Observer 的方法都是執行在 main 執行緒的,接下來就請各位讀者跟著筆者來分析了。
Observer#onSubscribe(Dispose)
看到標題部分讀者就疑惑了,明明是說執行緒切換,跟 Observer#onSubscribe()
方法有什麼關係呢?前方的 log 中展示 Observer#onSubscribe()
方法在主執行緒執行的,但是這個主執行緒是由 .observeOn(AndroidSchedulers.mainThread())
所導致的嗎?為了解決這個疑惑,我們可以在外面套一個子執行緒,然後去執行該邏輯,程式碼如下:
new Thread() {
@Override
public void run() {
Log.e("TAG", "run: 所線上程為 " + Thread.currentThread().getName());
// 新增示例程式碼
}
}.start();
複製程式碼
列印結果:
run: 所線上程為 Thread-554
onSubscribe(): 所線上程為 Thread-554
subscribe(): 所線上程為 RxCachedThreadScheduler-1
onNext(): 所線上程為 main
onComplete(): 所線上程為 main
複製程式碼
所以實際上 Observer#onSubscribe()
的執行執行緒是當前執行緒,它並不受 subscribe(Scheduler)
或 observeOn(Scheduler)
所影響(因為筆者這段程式碼寫在了 Android 主執行緒當中,所以當前執行緒是主執行緒)。本文不在此擴充套件原因,具體原始碼追溯和檢視前一篇文章,簡而言之—— subscribe(Observer)
-> subscribeActual(Observer)
-> Observer#onSubscribe()
,我們可以看到 subscribe(Observer)
的執行執行緒是當前執行緒,而在上面所述的資料流中也不存在資料切換的過程,所以 onSubscribe()
執行的執行緒也是當前執行緒。
Observable#observeOn(Scheduler)
此小節針對 Observable#observeOn(Scheduler)
講解,所以將示例程式碼更改如下:
new Thread() {
@Override
public void run() {
Log.e("TAG", "run: 當前預設執行環境為 " + Thread.currentThread().getName());
Observable
.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("1");
}
})
// 僅保留 observeOn(Scheduler)
.observeOn(Schedulers.io())
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String s) {
Log.e("TAG", "onNext(): 所線上程為 " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
} .start();
複製程式碼
輸出結果:
E/TAG: run: 當前預設執行執行緒為 Thread-610
E/TAG: onNext(): 所線上程為 RxCachedThreadScheduler-1
複製程式碼
不作用上游 Observable
同樣的,直接先進入 Observable#observeOn(Scheduler)
原始碼檢視一下,發現其最終會呼叫 Observable#的observeOn(Scheduler, boolean, int)
方法,該方法將會返回一個 Observable 物件。那麼老問題來了,是哪個 Observable 物件呼叫的 observeOn()
方法,又返回了一個怎樣的 Observable 物件?
第一個問題很簡單,是 Observable.create(ObservableOnSubscribe)
物件返回的一個 Observable,而且這個 Observable 是一個 ObservableCreate 物件(這裡不理解的可以檢視第一篇文章)。但是 Observable#observeOn(Scheduler, boolean, int)
是沒有被任何子類重寫的,這意味著它的子類都是呼叫它的該方法。
第二個問題來了,返回了一個怎樣的 Observable 物件呢?實際上這裡的分析流程和第一篇文章中所闡述的流程是一模一樣的,我們戳進 Observable#observeOn(Scheduler, boolean, int)
原始碼,發現它最終會返回一個 new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize)
物件,這裡我們只關注前兩個物件,第一個引數 this
是指上游的 Observable 物件,也就是我們第一個問題中所涉及到的 Observable 物件,第二個引數 scheduler
毋庸置疑就是我們所傳入的 Scheduler 物件了,在此也就是我們的 AndroidSchedulers.mainThread()
。
通過第一篇的學習,我們應該會輕車熟路地開啟 ObservableObserveOn 類並檢視它的核心 subscribeActual()
方法以及建構函式——
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) {
// 如果傳入的 scheduler 是 Scheduler.trampoline() 的情況
// 該執行緒的意義是傳入當前執行緒,也就是不做任何執行緒切換操作
if (scheduler instanceof TrampolineScheduler) {
source.subscribe(observer);
} else {
Scheduler.Worker w = scheduler.createWorker();
source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
}
}
複製程式碼
直接進入第二個 case,首先先略去第19行程式碼,看到第20行程式碼,source
(上游 Observable) 和 Observable#subscribe()
操作都沒有任何變化,唯一改變的地方就是將 Observer 進行了封裝,所以我們可以因此得出結論, Observable#observeOn(Scheduler)
並不會對上游執行緒執行環境有任何影響。(如果看到這裡不能夠理解的話,後文中會有通俗易懂的虛擬碼輔助理解)
作用下游 Observer
經過上文友好 RxJava2.x 原始碼解析(一)基本訂閱流程一文的分析我們知道 ObservableEmitter 的 onNext(T)
方法會觸發「下游」 Observer 的 onNext(T)
方法,而此時的「下游」 Observer 物件是經過 Observable#observeOn(Scheduler)
封裝的 ObserveOnObserver 物件,所以我們不妨開啟 ObserveOnObserver 的 onNext(T)
方法——
@Override
public void onNext(T t) {
// 刪除無關原始碼
queue.offer(t);
schedule();
}
複製程式碼
可以看到 onNext(T)
方法做了兩件事——一是將當前方法傳入的物件新增進佇列;另一是執行 schedule()
方法,開啟 schedule()
方法原始碼——
void schedule() {
// 刪除無關原始碼
worker.schedule(this);
}
複製程式碼
所以將會執行 worker.schedule(Runnable)
,可向下繼續追溯到 schedule(Runnable, long, TimeUnit )
,該方法是一個抽象方法,所以我們可以想到,排程器們就是通過實現該方法來建立各色各樣的執行緒的。所以我們繼續追溯到 IoScheduler 的 schedule(Runnable, long, TimeUnit)
中,原始碼如下:
public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
// 刪除無關原始碼
return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}
複製程式碼
繼續追溯下去——
@NonNull
public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
Future<?> f;
if (delayTime <= 0) {
f = executor.submit((Callable<Object>)sr);
} else {
f = executor.schedule((Callable<Object>)sr, delayTime, unit);
}
sr.setFuture(f);
return sr;
}
複製程式碼
executor 是一個 ScheduledExecutorService 物件,而 ScheduledExecutorService 的父介面是我們所熟悉的 ExecutorService 介面,所以很清晰 ScheduledExecutorService 具有建立和排程執行緒的能力,而其具體的實現在此就不討論了。
最後,我們不妨將上述所提到的幾段原始碼整體抽象結合一下:
@Override
public void onNext(T t) {
// 刪除無關原始碼
if (delayTime <= 0) {
f = executor.submit((Callable<Object>)this);
} else {
f = executor.schedule((Callable<Object>)this, delayTime, unit);
}
}
複製程式碼
總結一下:onNext(T)
方法會觸發 Scheduler 物件的 schedule(Runnable, long, TimeUnit)
,該方法是一個抽象方法,由子類實現,所以才有了多元多樣的 Schedulers.io()/Schedulers.computation()/Schedulers.trampoline()
等排程器,具體排程器的內部會使用相關的執行緒來 submit()
或者 schedule()
任務。解決完排程器的問題,那麼接下來就是看看 Runnable#run()
裡面的邏輯是什麼樣的,回到 ObserveOnObserver 中——
@Override
public void run() {
drainNormal();
}
複製程式碼
drainNormal()
原始碼如下:
void drainNormal() {
final SimpleQueue<T> q = queue;
final Observer<? super T> a = actual;
for (;;) {
T v;
try {
v = q.poll();
} catch (Throwable ex) {
}
boolean empty = v == null;
if (empty) {
break;
}
a.onNext(v);
}
}
複製程式碼
可以看到實際上最後一行執行了 Observer#onNext(T)
方法,也就是意味著「ObserveOnObserver 中觸發下一層 Observer 的 onNext(T)
操作」在指定執行緒執行,也就達到了切換執行緒的目的了。
來個複雜的例子——
經過友好 RxJava2.x 原始碼解析(一)基本訂閱流程一文我們知道,Observer 的傳遞是由下往上的,從源頭開始,我們自定義的 Observer 向上傳遞的時候到達第六個 Observable 的時候被執行緒封裝了一層,我們不妨使用虛擬碼演示一下——
public class Observer {
Observer oldObserver;
public Observer(Observer observer) {
oldObserver = observer;
}
public void onNext(T t) {
// 一些其他操作
new Thread("Android mainThread") {
@Override
public void run() {
oldObserver.onNext(t);
}
} .start();
}
public void onError(Throwable e) {
// 一些其他操作
new Thread("Android mainThread") {
@Override
public void run() {
oldObserver.onError(e);
}
} .start();
}
public void onComplete() {
// 一些其他操作
new Thread("Android mainThread") {
@Override
public void run() {
oldObserver.onComplete();
}
} .start();
}
}
複製程式碼
Observer 繼續向上被傳遞,Observable#map()
中並未對 Observer 進行執行緒切換;再向上走,到達第四個 observeOn(Scheduler)
的時候,被 computation 執行緒巢狀了一層——
public class Observer {
Observer oldObserver;
public Observer(Observer observer) {
oldObserver = observer;
}
public void onNext(T t) {
// 一些其他操作
new Thread("computation") {
@Override
public void run() {
oldObserver.onNext(t);
}
} .start();
}
public void onError(Throwable e) {
// 一些其他操作
new Thread("computation") {
@Override
public void run() {
oldObserver.onError(e);
}
} .start();
}
public void onComplete() {
// 一些其他操作
new Thread("computation") {
@Override
public void run() {
oldObserver.onComplete();
}
} .start();
}
}
複製程式碼
當然,繼續向上直到頂端 Observable——
public class Observer {
Observer oldObserver;
public Observer(Observer observer) {
oldObserver = observer;
}
public void onNext(T t) {
// 一些其他操作
new Thread("io") {
@Override
public void run() {
oldObserver.onNext(t);
}
} .start();
}
public void onError(Throwable e) {
// 一些其他操作
new Thread("io") {
@Override
public void run() {
oldObserver.onError(e);
}
} .start();
}
public void onComplete() {
// 一些其他操作
new Thread("io") {
@Override
public void run() {
oldObserver.onComplete();
}
} .start();
}
}
複製程式碼
甚至更精簡的操作如下:
new Thread("Scheduler io") {
@Override
public void run() {
// flatMap() 操作
flatMap();
System.out.println("flatMap 操作符執行執行緒:" + Thread.currentThread().getName());
System.out.println("第二個 observeOn() 執行執行緒:" + Thread.currentThread().getName());
// 第二個 observeOn() 操作
new Thread("Scheduler computation") {
@Override
public void run() {
// map() 操作
map();
System.out.println("map 操作符執行執行緒:" + Thread.currentThread().getName());
System.out.println("第三個 observeOn() 執行執行緒:" + Thread.currentThread().getName());
// 第三個 observeOn() 操作
new Thread("Android mainThread") {
@Override
public void run() {
// Observer#onNext(T)/onComplete()/onError() 執行執行緒
System.out.println("Observer#onNext(T)/onComplete()/onError() 執行執行緒:" +
Thread.currentThread().getName());
}
} .start();
}
} .start();
}
} .start();
複製程式碼
輸出結果:
flatMap 操作符執行執行緒:Scheduler io
第二個 observeOn() 執行執行緒:Scheduler io
map 操作符執行執行緒:Scheduler computation
第三個 observeOn() 執行執行緒:Scheduler computation
Observer#onNext(T)/onComplete()/onError() 執行執行緒:Android mainThread
複製程式碼
由此便將 Observable#observeOn(Scheduler)
是如何將下游 Observer 置於指定執行緒執行的流程分析完了。簡而言之 Observable#observeOn(Scheduler)
的實現原理在於將目標 Observer 的 onNext(T)/onError(Throwable)/onComplete()
置於指定執行緒中執行。
這裡特別要注意的一點是——【執行緒操作符切換的是其他的流,自身這條流是不會受到影響的。】看過知乎前一段時間的 rx 分享視訊的小夥伴應該有注意到楊凡前輩的 PPT 中有這麼一圖:
想要提出兩點——observeOn(Schedulers.io())
所對應的 Observable 應該是受到了subscribeOn(AndroidSchedulers.mainThread())
影響,所以它建立的這條流應該執行於主執行緒;而subscribeOn(AndroidSchedulers.mainThread())
所對應的 Observable 則受到了subscribeOn(Schedulers.computation)
影響,所以它建立的這條流應該執行於 computation 執行緒。
Observable#subscribeOn(Scheduler)
切換 subscribe 執行緒
示例程式碼:Observable
.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("1");
Log.e("TAG", "被觀察者所在的執行緒 " + Thread.currentThread().getName());
}
})
.subscribeOn(Schedulers.io())
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.e("TAG", "onSubscribe: " + Thread.currentThread().getName());
}
@Override
public void onNext(String s) {
Log.e("TAG", "觀察者所線上程為 " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
複製程式碼
輸出結果:
E/TAG: onSubscribe: main
E/TAG: 觀察者所線上程為 RxCachedThreadScheduler-1
E/TAG: 被觀察者所在的執行緒 RxCachedThreadScheduler-1
複製程式碼
同樣地,戳進 Observable#subscirbeOn(Scheduler)
原始碼,點進 ObservableSubscribeOn 檢視 subscribeActual(Observer)
的具體實現,相信這對於各位讀者來說已經輕車熟路了——
@Override
public void subscribeActual(final Observer<? super T> s) {
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
s.onSubscribe(parent);
Disposeable disposable = scheduler.scheduleDirect(new SubscribeTask(parent));
parent.setDisposable(disposable);
}
複製程式碼
第一行老套路,對下游 Observer 進行了一層封裝;第二行因為它不涉及執行緒切換所以此處也不做擴充套件;第三行就是我們的關鍵了 Scheduler#scheduleDirect(Runnable)
方法可以追溯到 Scheduler#schedule(Runnable, long, TimeUnit)
,這部分在前面已經闡述過了,就不做擴充套件了。SubscribeTask 是一個 Runnable,它的 run()
核心方法——
@Override
public void run() {
source.subscribe(parent);
}
複製程式碼
至此謎團解開了,Observable#subscribeOn(Scheduler)
將 Observable#subscribe(Observer)
的執行過程移到了指定執行緒(在上述中也就是 io 執行緒),同時 Observable 和 Observer 中並未做新的執行緒切換處理,所以它們的訂閱、發射等操作就執行在了 io 執行緒。
第一次有效原理
示例程式碼:Observable
.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("1");
Log.e("TAG", "被觀察者所在的執行緒 " + Thread.currentThread().getName());
}
})
.subscribeOn(Schedulers.io())
.subscribeOn(Schedulers.computation())
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.e("TAG", "onSubscribe: " + Thread.currentThread().getName());
}
@Override
public void onNext(String s) {
Log.e("TAG", "觀察者所線上程為 " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
複製程式碼
列印結果:
onSubscribe: main
觀察者所線上程為 RxCachedThreadScheduler-1
被觀察者所在的執行緒 RxCachedThreadScheduler-1
複製程式碼
我們知道,只有第一個 Observable#subscribeOn(Scheduler)
操作才有用,而後續的 Observable#subscribeOn(Scheduler)
並不會影響整個流程中 Observerable 。同樣的,來張圖——
前面我們分析到,Observable#subscribeOn(Scheduler)
實際上是將 Observable#subscribe(Observer)
的操作放在了指定執行緒,而通過友好 RxJava2.x 原始碼解析(一)基本訂閱流程一文我們知道了 subscribe
的過程是由下往上的。所以首先是第三個 Observable 呼叫 Observable#subscribe(Observer)
啟動訂閱,在其內部會啟用第二個 Observable 的 Observable#subscribe(Observer)
方法,但是此時該方法外部被套入了一個 Schedulers.computation()
執行緒,於是這個訂閱的過程就被執行在了該執行緒中。同樣的,我們不妨用虛擬碼演示一下——
public class Observable {
// 第「二」個 Observable
Observable source;
Observer observer;
public Observable(Observable source, Observer observer) {
this.source = source;
this.observer = observer;
}
public void subscribe(Observer Observer) {
new Thread("computation") {
@Override
public void run() {
// 第「二」個 Observable 訂閱
source.subscribe(observer);
}
}
}
}
複製程式碼
再往上走,第二個 Observable 訂閱內部會啟用第一個 Observable 的 Observable#subscribe(Observer)
方法,同樣的,該方法被套在了 Schedulers.io()
執行緒中,如下——
public class Observable {
// 第「一」個 Observable
Observable source;
Observer observer;
public Observable(Observable source, Observer observer) {
this.source = source;
this.observer = observer;
}
public void subscribe(Observer Observer) {
new Thread("io") {
@Override
public void run() {
// 第「一」個 Observable 訂閱
source.subscribe(observer);
}
}
}
}
複製程式碼
此時到達第一個 Observable 了之後就要開始發射事件了,此時的執行執行緒很明顯是 io 執行緒。還可以換成 Thread 虛擬碼來表示 ——
new Thread("computation") {
@Override
public void run() {
// 第二個 Observable.subscribe(Observer) 的實質
// 就是切換執行緒,效果類似如下
new Thread("io") {
@Override
public void run() {
// 第一個 Observable.subscribe(Observer) 的實質
// 就是發射事件
System.out.println("onNext(T)/onError(Throwable)/onComplete() 的執行執行緒是: " + Thread
.currentThread().getName());
}
} .start();
}
} .start();
複製程式碼
輸出結果:
onNext(T)/onError(Throwable)/onComplete() 的執行執行緒是: io
複製程式碼
Observable#observeOn(Scheduler) 和 Observable#subscribeOn(Scheduler)
如果針對前面的內容你已經懂了,那麼後續的內容可以直接跳過啦,本文就結束了~如果你還沒懂,筆者再彙總一次。經過友好 RxJava2.x 原始碼解析(一)基本訂閱流程一文我們知道,Observable#subscribe(Observer)
的順序是由下往上的,本遊會將 Observer 進行「封裝」,然後「啟用上游Observable 訂閱這個 Observer」。
我們不妨抽象一個 Observer,如下:
public class Observer<T> {
public void onNext(T t){}
public void onCompelete(){}
public void onError(Throwable t){}
}
複製程式碼
對於 Observable#observeOn(Schedulers.computation())
操作來說,它對 Observer 進行了怎樣的封裝呢?
public class NewObserver<T> {
// 下游 Observer
Observer downStreamObserver;
public NewObserver(Observer observer) {
downStreamObserver = observer;
}
public void onNext(T t) {
new Thread("computation") {
downStreamObserver.onNext(t);
}
}
public void onError(Throwable e) {
new Thread("computation") {
downStreamObserver.onError(e);
}
}
public void onComplete() {
new Thread("computation") {
downStreamObserver.onComplete();
}
}
}
複製程式碼
在 Observable#observeOn(Scheduler)
內部,其對下游的 Observer 進行了類似如上的封裝,這就導致了其「下游」 Observer 在指定執行緒內執行。所以 Observable#observeOn(Scheduler)
是可以多次呼叫並有效的。
而對於 Observable#subscribe(Scheduler)
來說,它並未對下游 Observer 進行封裝,但是對於「啟用上游 Observable 訂閱這個 Observer」這個操作它做了一點小小的手腳,也就是切換執行緒,我們抽象如下——
public class ComputationObservable {
public void subscribe(observer) {
new Thread("computation") {
// upstreamObservable 是上游 Observable,我們不妨假設是下文中所提到的 IOObservable
upstreamObservable.subscribe(observer);
}
}
}
複製程式碼
而當它在往上遇到了一個新的 Observable#subscribe(Scheduler)
操作的時候——
public class IOObservable {
public void subscribe(observer) {
new Thread("io") {
// upstreamObservable 是上游 Observable,我們不妨下文中所提到的 TopObservable
upstreamObservable.subscribe(observer);
}
}
}
複製程式碼
我們不妨假設此時已經到達了最頂端開始發射事件了——
public class TopObservable {
public void subscribe(observer) {
observer.onNext(t);
}
}
複製程式碼
此時的 Observer#onNext(t)
的執行環境當然就是由最後一個 subscribeOn(Scheduler)
操作符(此處的最後一個是指訂閱流程中的最後一個,它與實際寫程式碼的順序相反,也就是我們程式碼中的第一個 subscribeOn(Scheduler)
操作符)所決定的了,在上述虛擬碼中也就是 io 執行緒,虛擬碼對應的原始碼如下——
Observable
.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("1");
}
})
.subscribeOn(Schedulers.io())
.subscribeOn(Schedulers.computation())
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String s) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});複製程式碼