1.線程控制Scheduler(二)
1)Scheduler的API(二)
前面講到了,可以利用 subscribeOn()
結合 observeOn()
來實現線程控制,讓事件的產生和消費發生在不同的線程。可是在瞭解了 map()
flatMap()
等變換方法後,n能不能多切換幾次線程?
答案是:能。因爲observeOn()
指定的是 Subscriber 的線程,而這個Subscriber
並不是(嚴格說應該爲『不一定是』,但這裏不妨理解爲『不是』)subscribe()
參數中的 Subscriber
,而是observeOn()
執行時的當前 Observable
所對應的 Subscriber
,即它的直接下級Subscriber
。換句話說,observeOn()
指定的是它之後的操作所在的線程。因此如果有多次切換線程的需求,只要在每個想要切換線程的位置調用一次observeOn()
即可。上代碼:
Observable.just(1, 2, 3, 4) // IO 線程,由 subscribeOn() 指定
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.map(mapOperator) // 新線程,由 observeOn() 指定
.observeOn(Schedulers.io())
.map(mapOperator2) // IO 線程,由 observeOn() 指定
.observeOn(AndroidSchedulers.mainThread)
.subscribe(subscriber); // Android 主線程,由 observeOn() 指定
如上,通過 observeOn()
的多次調用,程序實現了線程的多次切換。
不過,不同於 observeOn()
,subscribeOn()
的位置放在哪裏都可以,但它是隻能調用一次的。
又有好事的(其實還是當初的我)問了:如果我非要調用多次 subscribeOn()
呢?會有什麼效果?
這個問題先放着,我們還是從 RxJava
線程控制的原理說起吧。
2)Scheduler的原理(二)
下面這個是 1.1.0
的 版本:
public final Observable<T> subscribeOn(Scheduler scheduler) {
if (this instanceof ScalarSynchronousObservable) {
return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
}
return nest().lift(new OperatorSubscribeOn<T>(scheduler));
}
public final Observable<T> observeOn(Scheduler scheduler) {
if (this instanceof ScalarSynchronousObservable) {
return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
}
return lift(new OperatorObserveOn<T>(scheduler));
}
可以看出其實 subscribeOn()
和 Observable()
的內部實現,也是用的lift()
。
下面這個 是1.1.6
的版本:
public final Observable<T> subscribeOn(Scheduler scheduler) {
if (this instanceof ScalarSynchronousObservable) {
return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
}
return create(new OperatorSubscribeOn<T>(this, scheduler));
}
public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
if (this instanceof ScalarSynchronousObservable) {
return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
}
return lift(new OperatorObserveOn<T>(scheduler, delayError, bufferSize));
}
具體看圖(不同顏色的箭頭表示不同的線程
):
subscribeOn()
原理圖:
observeOn()
原理圖:
從圖中可以看出,subscribeOn()
和observeOn()
都做了線程切換的工作(圖中的 "schedule..."
部位)。不同的是, subscribeOn()
的線程切換髮生在OnSubscribe
中,即在它通知上一級 OnSubscribe
時,這時事件還沒有開始發送,因此 subscribeOn()
的線程控制可以從事件發出的開端就造成影響;而 observeOn()
的線程切換則發生在它內建的 Subscriber
中,即發生在它即將給下一級Subscriber
發送事件時,因此 observeOn()
控制的是它後面的線程。
(1) subscribeOn()
版本爲1.1.0
和1.1.6
的 內部源碼實現不同
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.6'
compile 'io.reactivex:rxandroid:1.2.1'
❶ 這邊是版本1.1.0的源碼
首先判斷當前observable
是不是ScalarSynchronousObservable
類型的,如果是則會直接執行scalarScheduleOn
方法,如果不是,則會通過nest()
方法創建一個ScalarSynchronousObservable
並把當前的Observable
包裝進去,並且lift
一個OperatorSubscribeOn
進去。
public final Observable<T> subscribeOn(Scheduler scheduler) {
if (this instanceof ScalarSynchronousObservable) {
return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
}
return nest().lift(new OperatorSubscribeOn<T>(scheduler));
}
subscribeOn 就是把 一個Observable
轉爲 另一個 Observable
, 那
一步步分析:
①
nest()
做了什麼呢?看下面源碼可以知道,他創建了一個ScalarSynchronousObservable
public final Observable<Observable<T>> nest() {
return just(this);
}
public final static <T> Observable<T> just(final T value) {
return ScalarSynchronousObservable.create(value);
}
②
OperatorSubscribeOn
做了什麼? 看他構造方法可以知道他,保存了一個scheduler
對象 ,注意看他的call
方法的 ,它運行時的線程,在inner
這個Worker
上,於是它的運行線程已經被改了
public class OperatorSubscribeOn<T> implements Operator<T, Observable<T>> {
private final Scheduler scheduler;
public OperatorSubscribeOn(Scheduler scheduler) {
this.scheduler = 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() {}
@Override
public void onError(Throwable e) {
subscriber.onError(e);
}
@Override
public void onNext(final Observable<T> o) {
// inner.schedule 就是在這邊進行線程切換,
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) {
producer.request(n);
} else {
inner.schedule(new Action0() {
@Override
public void call() {
producer.request(n);
}
});
}
}
});
}
});
}
});
}
};
}
}
③
但是 OperatorSubscribeOn
的call 方法在什麼時候調用呢?那我們就要來看下lift()
, 就是在hook.onLift(operator).call(o)
這邊進行調用的,
public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
return new Observable<R>(new OnSubscribe<R>() {
@Override
public void call(Subscriber<? super R> o) {
try {
// 就是在這邊調用的哦!!!!切換了線程
Subscriber<? super T> st = hook.onLift(operator).call(o);
try {
st.onStart();
onSubscribe.call(st);
} catch (Throwable e) {
Exceptions.throwIfFatal(e);
st.onError(e);
}
} catch (Throwable e) {
Exceptions.throwIfFatal(e);
o.onError(e);
}
}
});
}
❷ 這邊是版本1.1.6的源碼 建議看這個
public final Observable<T> subscribeOn(Scheduler scheduler) {
if (this instanceof ScalarSynchronousObservable) {
return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
}
return create(new OperatorSubscribeOn<T>(this, scheduler)); // 這邊是重點,就是這邊修改了
}
把
Observable.create()
創建的稱之爲Observable_1
,OnSubscribe_1
。把subscribeOn()
創建的稱之爲Observable_2
,OnSubscribe_2(= OperatorSubscribeOn)
。那麼,前兩步就是創建了兩個的
observable
,和OnSubscribe
,並且OnSubscribe_2
中保存了Observable_1
的引用,即下面代碼的source
(上面代碼的this
)。
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() {
final Thread t = Thread.currentThread();
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();
}
}
@Override
public void setProducer(final Producer p) {
subscriber.setProducer(new Producer() {
@Override
public void request(final long n) {
if (t == Thread.currentThread()) {
p.request(n);
} else {
inner.schedule(new Action0() {
@Override
public void call() {
p.request(n);
}
});
}
}
});
}
};
source.unsafeSubscribe(s);
}
});
}
}
我們看到它實現OnSubscribe
,並保存了原來的Observable
和Scheduler
。創建一個用於在不同線程執行的Worker
對象,然後worker
調用schedule()
去執行Observable1
的subscribe()
。
再來看subscribeOn
中調用的create
方法
public static <T> Observable<T> create(OnSubscribe<T> f) {
return new Observable<T>(hook.onCreate(f));
}
//hook的onCreate方法
public <T> OnSubscribe<T> onCreate(OnSubscribe<T> f) {
return f;
}
沒做什麼,就是把使用剛剛創建OperatorSubscribeOn
作爲參數,返回給方法鏈下一個。 那到這裏我們就清晰了,其實調用了subscribeOn
後,返回的Observable
已經不是最開始創建那個。我們來總結一下,先把create
創建的Observable
稱爲Observable1
,把它的OnSubscribe
稱爲OnSubscribe1
,把subscribeOn
中創建的Observable
稱爲Observable2
,把它的OnSubscribe(OperatorSubscribeOn implements OnSubscribe)
稱爲OnSubscribe2
。
public final Subscription subscribe(Subscriber<? super T> subscriber) {
return Observable.subscribe(subscriber, this);
}
在方法鏈最後調用了subscribe()
,應該是調用了Observable2
的subscribe()
,那這時候就會調用到OnSubscribe2
的call
方法,call
中的參數就我們在方法鏈中傳進來的。call
中會使用worker
切換線程去執行,到最後纔是調用了OnSubscribe1
的unsafeSubscribe方
法,並把重新封裝的Subscriber
傳過去。而unsafeSubscribe
會調用OnSubscribe1
的call()
,所以其他流程是不變的,只是subscribe()
執行的線程改了。
那爲什麼多次調用subscribeOn
是無效的呢?因爲多調用一次,只是多了一層先把OnSubscribe
,但其實會對Observable1
的subscribe()
產生影響的是OnSubscribe2
,其他的只會對它前面的產生影響。就好比下面這樣
new Thread() {
@Override
public void run() {
new Thread() {
@Override
public void run() {
Log.i(TAG,"run");
}
}.start();
}
}.start();
(2) observeOn()
1.1.6
版本源碼
public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
if (this instanceof ScalarSynchronousObservable) {
return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
}
return lift(new OperatorObserveOn<T>(scheduler, delayError, bufferSize));
}
observeOn()
這邊用到的是lift()
(這邊的lift()
和1.1.0
的 lift()
,略有差別,但是本質call
方法是一樣的),在這邊 創建了一個新的 Onsubscribe(=OnSubsribeLift)
public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
return new Observable<R>(new OnSubscribeLift<T, R>(onSubscribe, operator));
}
public final class OnSubscribeLift<T, R> implements OnSubscribe<R> {
static final RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook();
final OnSubscribe<T> parent;
final Operator<? extends R, ? super T> operator;
public OnSubscribeLift(OnSubscribe<T> parent, Operator<? extends R, ? super T> operator) {
this.parent = parent;
this.operator = operator;
}
@Override
public void call(Subscriber<? super R> o) {
try {
Subscriber<? super T> st = hook.onLift(operator).call(o);
try {
st.onStart();
parent.call(st);
} catch (Throwable e) {
Exceptions.throwIfFatal(e);
st.onError(e);
}
} catch (Throwable e) {
Exceptions.throwIfFatal(e);
o.onError(e);
}
}
}
可以看到在lift
中調用了OperatorObserveOn
的call
,傳進去Subscriber
創建了一個新的Subscriber
,再將其傳入onSubscribe.call()
中,說明了這個新的Subscriber.onNext()
,會被調用,而且它肯定在裏面還調用了原來的Subscriber.onNext()
方法。 到OperatorObserveOn
中就會發現,這個是新的Subscriber
是OperatorObserveOn
的內部類ObserveOnSubscriber
。
public ObserveOnSubscriber(Scheduler scheduler, Subscriber<? super T> child, boolean delayError) {
this.child = child;
this.recursiveScheduler = scheduler.createWorker();
....... }
void init() {
......
Subscriber<? super T> localChild = child;
......
localChild.add(recursiveScheduler);
localChild.add(this);
}
@Override
public void onStart() {
......
}
@Override
public void onNext(final T t) {
......
schedule();
}
@Override
public void onCompleted() {
......
schedule();
}
@Override
public void onError(final Throwable e) {
......
schedule();
}
protected void schedule() {
if (counter.getAndIncrement() == 0) {
recursiveScheduler.schedule(this);
}
}
// only execute this from schedule()
@Override
public void call() {
......
for (;;) {
if (checkTerminated(finished, q.isEmpty(), localChild, q)) {
return;
}
......
localChild.onNext(localOn.getValue(v));
......
}
}
boolean checkTerminated(boolean done, boolean isEmpty, Subscriber<? super T> a, Queue<Object> q) {
if (a.isUnsubscribed()) {
......
if (e != null) {
a.onError(e);
} else {
a.onCompleted();
}
......
return false;
}
}
我們看到onNext
、onCompleted
、onError
中都調用了schedule
,schedule
中又調用了recursiveScheduler.schedule(this)
,recursiveScheduler
就是根據我們設置的線程模式生成的Worker
,在這裏線程就切換到我們指定的那邊了。然後又調用到this.call()
,在call
裏面又調用了localChild.onNext
、onError
、onCompleted
,這個localChild
就是原來的Subscriber
,所以onNext
、onError
、onCompleted
就會在我們指定的線程調用了。
那爲什麼會對observeOn
後面的map
、flatMap
起作用,因爲map
裏面其實也是調用了lift去創建一個新的Subscriber
,所以也就會在新的線程中執行。
爲什麼每次observeOn
都會起作用,因爲它跟下面這個差不多。
new Thread() {
@Override
public void run() {
map();
new Thread() {
@Override
public void run() {
map();
onNext();
}
}.start();
}
}.start();
(3) 延伸:doOnSubscribe()
然而,雖然超過一個的 subscribeOn()
對事件處理的流程沒有影響,但在流程之前卻是可以利用的。
在前面講Subscriber
的時候,提到過 Subscriber
的 onStart()
可以用作流程開始前的初始化。然而 onStart()
由於在subscribe()
發生時就被調用了,因此不能指定線程,而是隻能執行在 subscribe()
被調用時的線程。這就導致如果onStart()
中含有對線程有要求的代碼(例如在界面上顯示一個 ProgressBar
,這必須在主線程執行),將會有線程非法的風險,因爲有時你無法預測 subscribe()
將會在什麼線程執行。
而與 Subscriber.onStart()
相對應的,有一個方法Observable.doOnSubscribe()
。它和 Subscriber.onStart()
同樣是在subscribe()
調用後而且在事件發送前執行,但區別在於它可以指定線程。默認情況下, doOnSubscribe() 執行在 subscribe()
發生的線程;而如果在doOnSubscribe()
之後有 subscribeOn()
的話,它將執行在離它最近的 subscribeOn()
所指定的線程。
示例代碼:
Observable.create(onSubscribe)
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Action0() {
@Override
public void call() {
progressBar.setVisibility(View.VISIBLE); // 需要在主線程執行
}
})
.subscribeOn(AndroidSchedulers.mainThread()) // 指定主線程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
如上,在 doOnSubscribe()
的後面跟一個 subscribeOn()
,就能指定準備工作的線程了。