在RxJava中,有個很重要的概念叫做”執行緒排程器”—Scheduler。它用一種隱式的方法遮蔽掉了我們之前通過回撥方式的執行緒呼叫。我們先看個例子:
Observable<String> ob = Observable.just("str1","str2");
ob.map(new Func1<String, String>() {
@Override
public String call(String t) {
System.out.println("function call " + Thread.currentThread());
return "[" + t + "]";
}})
.observeOn(Schedulers.newThread())
.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {}
@Override
public void onError(Throwable e) {}
@Override
public void onNext(String t) {
System.out.println("onNext call " + Thread.currentThread());
System.out.println("onNext "+t);
}
});複製程式碼
程式碼中,我們通過一個字串生成了一個Observable物件,而這個物件我們又通過一個map對映對映成為一個新的Observable物件(這部分的知識請參照第一章RxJava原始碼解析(一)從一個例子開始)。在這之後,我們有通過呼叫observeOn方法設定了一個叫做Schedulers.newThread()的排程器。這個函式的目的是為了告訴你的被觀察者,當你的資料返回的時候需要往哪個執行緒上post你的資料訊息,換句話說,也就是你所定義的Subscriber物件的onCompleted/onError/onNext的執行執行緒。這段程式碼最後輸出:
//output:
function call Thread[main,5,main]//map對映發生在預設執行緒也就是虛擬機器主執行緒中
function call Thread[main,5,main]//map對映發生在預設執行緒也就是虛擬機器主執行緒中
onNext call Thread[RxNewThreadScheduler-1,5,main] // 訊息回撥函式處理在一個新的執行緒中
onNext [str1]
onNext call Thread[RxNewThreadScheduler-1,5,main] // 訊息回撥函式處理在一個新的執行緒中
onNext [str2]複製程式碼
本章,我們將重點關注這個排程器,那麼我們首先要思考的問題是,這個排程器將會提供什麼功能呢?這就要回頭看下我們能用這個排程器幹什麼了?
首先,我們需要排程器去幫助我們生成一個執行緒
其次,當我們以後得到了結果,我們還要需要排程器往排程器執行緒中傳送一個訊息,以便可以執行訂閱者的回撥函式好的,基於我們上面的需求,我們將看下,在RxJava的排程器實現中,是如何實現我們所需要的功能的。
我們先來看下Observable物件所提供的observeOn函式,這個函式有多個函式過載,最終都會呼叫到三個引數的observeOn方法:
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));
}複製程式碼
這裡呼叫到了RxJava中一個很重要的操作符號
lift
。lift
函式引入了一個叫做Operator
的新型別,在上述的例子中這個型別的實現類是一個叫做OperatorObserveOn
的策略型別。我們看下這個lift
函式定義:
public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
return unsafeCreate(new OnSubscribeLift<T, R>(onSubscribe, operator));
}複製程式碼
我們所傳入的
Operator
物件最終被包裝成為一個OnSubscribeLift
物件,OnSubscribeLift
物件是我們非常熟悉的OnSubscribe
型別的子類。第一章我們說到OnSubscribe
提供一種處理訂閱者註冊訂閱後的策略。按照我們上面的例子,我們呼叫過map
函式後呼叫observeOn
函式,此時傳入的onSubscribe
對應的就是map
產生的OnSubscribeMap
物件。而引數operator
對應observeOn
函式中的OperatorObserveOn
物件。我們先來看下Operator
類的定義:
public interface Operator<R, T> extends Func1<Subscriber<? super R>, Subscriber<? super T>> {
// cover for generics insanity
}複製程式碼
Operator
也是一種對映關係函式,轉換型別是通過Subscriber<T>->Subscriber<R>
。也就是說,Operator
是一個直接轉化新的Subscriber
的對映函式。這樣就可以在訂閱前攔截訂閱操作。比如:
Observable<String> ob = Observable.just("str1","str2");
ob.map(new Func1<String, String>() {
@Override
public String call(String t) {
System.out.println("function call " + Thread.currentThread());
return "[" + t + "]";
}})
.lift(new Operator<String,String>(){
@Override
public Subscriber<String> call(Subscriber<? super String> st) {
return new Subscriber<String>() {
@Override
public void onNext(String t) {
long startTime = System.currentTimeMillis();
System.out.println("onNext begin");
st.onNext(t);//用於監控訂閱者的執行時間
System.out.println("onNext execute on next time = "+(System.currentTimeMillis() - startTime)+"ms");
}
};
}})
.subscribe(new Subscriber<String>() {
@Override
public void onNext(String t) {
IO.waitTime(5000);
System.out.println("call onNext "+t);
}
});複製程式碼
比如,我們為了監控訂閱者訂閱的時候有多少的時間消耗,我們通過
lift
函式在我們的訂閱者外包裝了一層Subscriber
,這樣我們就可以依賴於包裝的Subscriber
物件進行函式監控:
//output:
function call Thread[main,5,main]
onNext begin//開啟監控
call onNext [str1]
onNext execute on next time = 5005ms//監控結束複製程式碼
也就是說,上述的例子中我們的流程圖應該是:
好的,有了上面的概念,我們可以來看下OperatorObserveOn的程式碼,我們看下它給我們生成了一個什麼樣的訂閱者:
@Override
public Subscriber<? super T> call(Subscriber<? super T> child) {
....
ObserveOnSubscriber<T> parent = new ObserveOnSubscriber<T>(scheduler, child, delayError, bufferSize);
parent.init();
return parent;
.....
}複製程式碼
Lift
函式執行完後,會將我們所註冊的Subscriber
裝飾成為一個ObserveOnSubscriber
物件。”lift後流程圖”的紅色框框部分以後註明了這個物件的功能。我們先來看下ObserveOnSubscriber
物件的onCompleted/onError
三個方法:
@Override
public void onCompleted() {
if (isUnsubscribed() || finished) {
return;
}
finished = true;
schedule();
}
@Override
public void onError(final Throwable e) {
if (isUnsubscribed() || finished) {
RxJavaHooks.onError(e);
return;
}
error = e;
finished = true;
schedule();
}複製程式碼
由於
onCompleted
和onError
是互斥的,且只會被呼叫一次,因此會用一個finished
的boolean
變數來進行攔截,然後呼叫schedule()
函式來處理剩下邏輯:
final AtomicLong counter = new AtomicLong();
protected void schedule() {
if (counter.getAndIncrement() == 0) {
recursiveScheduler.schedule(this);
}
}複製程式碼
由於
counter
在呼叫getAndIncrement()
後就大於0,因此recursiveScheduler.schedule(this)
只會被呼叫一次,recursiveScheduler
的定義在ObserveOnSubscriber
的構造器中:
public ObserveOnSubscriber(Scheduler scheduler, Subscriber<? super T> child, boolean delayError, int bufferSize) {
this.child = child;
this.recursiveScheduler = scheduler.createWorker();
...
}複製程式碼
scheduler
就是我們傳入的Schedulers.newThread()
物件,實際上是一個NewThreadScheduler
物件:
@Override
public Worker createWorker() {
return new NewThreadWorker(threadFactory);//recursiveScheduler的型別是一個NewThreadWorker
}複製程式碼
可以看出,
recursiveScheduler
最終會被置為NewThreadWorker
型別
public NewThreadWorker(ThreadFactory threadFactory) {
ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, threadFactory);
....
executor = exec;
}複製程式碼
NewThreadWorker
構造器中,定義了一個核心執行緒為1的ScheduledThreadPool
執行緒池。(ScheduledThreadPool
是一個很特殊的執行緒池,這個執行緒池的主要是為了支援延遲任務,或者定時任務。)recursiveScheduler.schedule(this)
實際上就是呼叫NewThreadWorker
的schedule(Action0)
方法。
@Override
public Subscription schedule(final Action0 action) {
return schedule(action, 0, null);
}
@Override
public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit) {
if (isUnsubscribed) {
return Subscriptions.unsubscribed();
}
return scheduleActual(action, delayTime, unit);
}
public ScheduledAction scheduleActual(final Action0 action, long delayTime, TimeUnit unit) {
....
ScheduledAction run = new ScheduledAction(decoratedAction);
Future<?> f;
if (delayTime <= 0) {
f = executor.submit(run);
} else {
f = executor.schedule(run, delayTime, unit);
}
...
}複製程式碼
schedule
方法最終會呼叫到scheduleActual
方法,action
物件會被包裝成為一個ScheduledAction
的Runable
物件提交給執行緒池executor
。而執行緒池會呼叫ScheduledAction
的run()
方法,在run()
方法中,又會呼叫Action0
的call()
方法:
@Override
public void run() {
try {
.....
action.call();
} catch (OnErrorNotImplementedException e) {
....
}
}複製程式碼
如果剛才的程式碼已經把你給繞懵了,不要緊,我們再來回顧一下流程:
1. 我們通過lift函式註冊了一個叫做
OperatorObserveOn
的Operator
物件2. lift函式會構造一個叫做
OnSubscribeLift
的物件用於構造一個Observable
物件3. 當訂閱者
Subscriber
物件訂閱Observable
的時候,根據呼叫鏈,會優先使用OnSubscribeLift
物件作為優先處理物件。4
OnSubscribeLift
呼叫call(Subscriber)
方法,在該類的call方法中,會通過內部的Operator
物件(也就是OperatorObserveOn
物件)的Subscriber call(Subscriber)
方法,生成一個新的訂閱者ObserveOnSubscriber
5. 新的訂閱者物件
ObserveOnSubscriber
被OnSubscribeLift物件傳遞給上層的OnSubscribe
物件處理,也就是走如RxJava原始碼解析(一)從一個例子開始)中的流程,最後會走到OnSubscribeFromArray
物件中,然後遍歷裡面的陣列生產者6.
OnSubscribeFromArray
遍歷陣列中的成員,然後呼叫訂閱者的onNext
和onCompleted
。而最終要呼叫到的訂閱者就是ObserveOnSubscriber
物件。7.
ObserveOnSubscriber
物件的onNext()
和onCompleted()
方法會觸發執行schedule()
方法,schedule()
方法會呼叫Scheduler.Worker.schedule(Action0)
方法,而這個Action0
物件就是ObserveOnSubscriber
型別8. 當我們選擇
Schedulers.newThread()
排程器的時候,Scheduler.Worker
物件實際型別為NewThreadWorker
物件,而NewThreadWorker.schedule(Action0)
中會將Action0
物件包裝成為ScheduledAction
物件,ScheduledAction
本質是一個Runnable
型別,因此它可以被提交到執行緒池中,呼叫ScheduledAction.run()
方法,而ScheduledAction.run()
方法中,又會呼叫Action0.call()
9. 步驟8中
Action0
實現的型別為ObserveOnSubscriber
型別,此時呼叫ObserveOnSubscriber.call()
方法會從queue
佇列中讀取onNext
引數值並檢測是否已經結束,注意,由於當前函式是由我們排程器生成的Worker
物件中的執行緒池呼叫的,因此當前的全部回撥操作都發生在Worker
所構建的執行緒中。#####總結
實際上,我們從上面可以看出,我們通過lift函式所構造出來的ObserveOnSubscriber
物件,實際上是生成了一個OnSubscriber
的裝飾物件。而這個物件的具體操作,都被封裝到了call()
方法中去,換句話說,我們的排程器實際上就是提供一個容器,給我們的call()
方法提供上下文。基於我們上述的結論,我們實際上就可以寫出我們自己的排程器:
private static class SchedulerImpl extends Scheduler {
@Override
public Worker createWorker() {
// TODO Auto-generated method stub
return new WorkerImpl();
}
}
private static class WorkerImpl extends Scheduler.Worker {
@Override
public void unsubscribe() {}
@Override
public boolean isUnsubscribed() {
return false;
}
@Override
public Subscription schedule(Action0 action) {
return schedule(action,0,null);
}
@Override
public Subscription schedule(Action0 action, long delayTime, TimeUnit unit) {
Thread thread = new Thread() {
public void run() {
action.call();
};
};
thread.setName("test");
thread.start();
return null;
}
}複製程式碼
這個排程器的寫法非常的簡單:
1.我們先構建一個Scheduler
用於管理我們的Worker
2.observeOn
會給我們提供一個ObserveOnSubscriber
型別的Action0
物件,作為引數呼叫Worker.schedule(Action0 action)
方法
3.我們生成了一個獨立的執行緒"test"
,並線上程中呼叫Action0.call ()
方法,這樣就可以將事件傳送到我們所訂閱的真正的Subscriber
上了最後輸出日誌:
output:
call onNext [str1]
call onNext [str2]
call onCompleted Thread[test,5,main]