前言
通過前一篇的從觀察者模式出發,聊聊RxJava,我們大致理解了RxJava的實現原理,在RxJava中可以非常方便的實現不同執行緒間的切換。subscribeOn 用於指定上游執行緒,observeOn 用於指定下游執行緒,多次用 subscribeOn 指定上游執行緒只有第一次有效,多次用 observeOn 指定下次執行緒,每次都有效;簡直太方便了,比直接使用Handler省了不少力氣,同時也不用去關注記憶體洩漏的問題了。本篇就來看看在RxJava中上游是如何實現執行緒切換。
RxJava 基礎原理
為了方便後面的敘述,這裡通過下面的UML圖簡單回顧一下上一篇的內容。
此圖並沒有完整的展現圖中各個介面和類之間的各種關係,因為那樣會導致整個圖錯綜複雜,不便於檢視,這裡只繪製出了RxJava各個類之間核心關係網路
從上面的UML圖中可以看出,具體的實現類只有ObservableCreate和CreateEmitter。CreateEmitter是ObservableCreate的內部類(PlantUML 怎麼繪製內部類,沒搞懂,玩的轉的同學請賜教呀(^▽^))。
上篇說過Observable建立的過程,可以簡化如下:
Observable mObservable=new ObservableCreate(new ObservableOnSubscribe())
複製程式碼
結合圖可以更直觀的體現出這一點。ObservableCreate 內部持有ObservableOnSubscribe的引用。
當觀察者訂閱主題後:
mObservable.subscribe(mObserver);
複製程式碼
ObservableCreate 中的subscribeActual()方法就會執行,
protected void subscribeActual(Observer<? super T> observer) {
CreateEmitter<T> parent = new CreateEmitter<T>(observer);
observer.onSubscribe(parent);
try {
source.subscribe(parent);
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
parent.onError(ex);
}
}
複製程式碼
在這個過程中會建立CreateEmitter 的例項,而這個CreateEmitter實現了Emitter和Disposable介面,同時又持有Observer的引用(當然這個引用是ObservableCreate傳遞給他的)。接著就會執行ObservableOnSubscribe的subscribe 方法,方法的引數即為剛剛建立的CreateEmitter 的例項,接著一系列連鎖反應,Emitter 介面中的方法(onNext,onComplete等)開始執行,在CreateEmitter內部,Observer介面中對應的方法依次執行,這樣就實現了一次從主題(上游)到觀察者(下游)的事件傳遞。
source.subscribe(parent)
這裡的 source 是ObservableOnSubscribe的例項,parent是CreateEmitter的例項。上面加粗文字敘述的內容,就是這行程式碼,可以說這是整個訂閱過程最核心的實現。
好了,回顧完基礎知識後,馬上進入正題,看看RxJava是如何實現執行緒切換的。
RxJava 之 subscribeOn
我們知道正常情況下,所有的內容都是在主執行緒執行,既然這裡提到了執行緒切換,那麼必然是切換到了子執行緒,因此,這裡需要關注執行緒的問題,我們就帶著下面這幾個問題去閱讀程式碼。
- 1.是哪個物件在什麼時候建立了子執行緒,是一種怎樣的方式建立的?
- 2.子執行緒又是如何啟動的?
- 3.上游事件是怎麼跑到子執行緒裡執行的?
- 4.多次用 subscribeOn 指定上游執行緒為什麼只有第一次有效 ?
示例
首先看一下,日常開發中實現執行緒切換的具體實現
private void multiThread() {
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
e.onNext("This msg from work thread :" + Thread.currentThread().getName());
sb.append("\nsubscribe: currentThreadName==" + Thread.currentThread().getName());
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, "accept: s= " + s);
}
});
}
複製程式碼
這段程式碼,使用過RxJava的同學再熟悉不過了,上游事件會在一個名為 RxNewThreadScheduler-1 的執行緒執行,下游執行緒會切換回我們熟悉的Android UI執行緒。
我們就從subscribeOn(Schedulers.newThread()) 出發,看看這個程式碼的背後,到底發生了什麼。
subscribeOn
這裡我們先不管Schedulers.newThread() 是什麼鬼,首先看看這個subscribeOn()方法。
Observable.java--- subscribeOn(Scheduler scheduler)
public final Observable<T> subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
}
複製程式碼
可以看到,這個方法需要一個Scheduler 型別的引數。
RxJavaPlugins.java--- onAssembly(@NonNull Observable source)
public static <T> Observable<T> onAssembly(@NonNull Observable<T> source) {
Function<? super Observable, ? extends Observable> f = onObservableAssembly;
if (f != null) {
return apply(f, source);
}
return source;
}
複製程式碼
O(∩_∩)O哈哈~,是不是覺得似曾相識,和create操作符一個套路呀。因此,observeOn也可以簡化如下:
new ObservableSubscribeOn<T>(this, Schedulers.newThread());
複製程式碼
這裡你也許會有疑問,這個this是什麼呢?其實這個this就是Observable,具體到上面的程式碼來說就是ObservableCreate,總之就是一個具體的Observable。
接著看ObservableSubscribeOn 這個類
public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
}
複製程式碼
看一下 AbstractObservableWithUpstream.java
abstract class AbstractObservableWithUpstream<T, U> extends Observable<U> implements HasUpstreamObservableSource<T> {
/** The source consumable Observable. */
protected final ObservableSource<T> source;
AbstractObservableWithUpstream(ObservableSource<T> source) {
this.source = source;
}
@Override
public final ObservableSource<T> source() {
return source;
}
}
複製程式碼
再看一下 HasUpstreamObservableSource.java
/**
* Interface indicating the implementor has an upstream ObservableSource-like source available
* via {@link #source()} method.
*
* @param <T> the value type
*/
public interface HasUpstreamObservableSource<T> {
/**
* Returns the upstream source of this Observable.
* <p>Allows discovering the chain of observables.
* @return the source ObservableSource
*/
ObservableSource<T> source();
}
複製程式碼
饒了半天,ObservableSubscribeOn 原來和上一篇說的ObservableCreate一樣,也是Observable的一個子類。只不過比ObservableCreate多實現了一個介面HasUpstreamObservableSource,這個介面很有意思,他的source()方法返回型別是ObservableSource(還記得這個類的角色嗎?)。也就是說ObservableSubscribeOn這個Observable是一個擁有上游的Observable。他有一個非常關鍵的屬性source,這個source就代表了他的上游。
我們接著看ObservableSubscribeOn的具體實現。
public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
final Scheduler scheduler;
public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
super(source);
this.scheduler = scheduler;
}
@Override
public void subscribeActual(final Observer<? super T> s) {
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
// observer 呼叫onSubscribe方法,獲取上游的控制權
s.onSubscribe(parent);
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
}
複製程式碼
- 首先看他的建構函式,引數source就是我們之前提到過的this,scheduler就是Schedulers.newThread()。同時呼叫了父類AbstractObservableWithUpstream的建構函式,這裡結合之前的結論,我們可以確定通過這個建構函式,就建立出來了一個包含上游的ObservableSubscribeOn例項。
- 再看實現訂閱關係的關鍵方法subscribeActual(),在這裡建立了一個SubscribeOnObserver的例項,SubscribeOnObserver 是AtomicReference的子類(保證原子性),同時實現了 Observer介面 和 Disposable 介面;你可以把他理解成一個Observer。
我們之前說過,subscribeActual()是實現上下游之間訂閱關係的重要方法。因為只有真正實現了訂閱關係,上下游之間才能連線起來。我們看這個方法的最後一句程式碼。
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
複製程式碼
這句程式碼,可以說就是非常關鍵,因為從這裡開始了一系列的連鎖反應。首先看一下SubscribeTask
final class SubscribeTask implements Runnable {
private final SubscribeOnObserver<T> parent;
SubscribeTask(SubscribeOnObserver<T> parent) {
this.parent = parent;
}
@Override
public void run() {
source.subscribe(parent);
}
}
複製程式碼
看到這句 source.subscribe(parent),是不是覺得似曾相識呢?
SubscribeTask 實現了是Runnable介面,在其run方法中,定義了一個需要線上程中執行的任務。按照類的繼承關係,很明顯source 就是ObservableSubscribeOn 的上游Observable,parent是一個Observer。也就是說這個run方法要執行的內容就是實現ObservableSubscribeOn的上游和Observer的訂閱。一旦某個執行緒執行了這個Runnable(SubscribeTask),就會觸發了這個run方法,從而實現訂閱,而一旦這個訂閱實現,那麼後面的流程就是上節所說的事情了。
這裡可以解答第三個問題了,上游事件是怎麼給弄到子執行緒裡去的,這裡很明顯了,就是直接把訂閱方法放在了一個Runnable中去執行,這樣就一旦這個Runnable在某個子執行緒執行,那麼上游所有事件只能在這個子執行緒中執行了。
好了,執行緒要執行的任務似乎建立完了,下面就接著找看看子執行緒是怎麼建立的。回過頭繼續看剛才的方法,
scheduler.scheduleDirect(new SubscribeTask(parent))
複製程式碼
Scheduler.java----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();
// 對run進行了一次裝飾
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
DisposeTask task = new DisposeTask(decoratedRun, w);
w.schedule(task, delay, unit);
return task;
}
@NonNull
// 抽象方法
public abstract Worker createWorker();
複製程式碼
首先看一下Worker類
/**
* Sequential Scheduler for executing actions on a single thread or event loop.
* <p>
* Disposing the {@link Worker} cancels all outstanding work and allows resource cleanup.
*/
public abstract static class Worker implements Disposable {
@NonNull
public Disposable schedule(@NonNull Runnable run) {
return schedule(run, 0L, TimeUnit.NANOSECONDS);
}
@NonNull
public abstract Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit);
}
複製程式碼
Worker是Scheduler內部的一個靜態抽象類,實現了Disposable介面,其schedule()方法也是抽象的。
再看一下DisposeTask
static final class DisposeTask implements Runnable, Disposable {
final Runnable decoratedRun;
final Worker w;
Thread runner;
DisposeTask(Runnable decoratedRun, Worker w) {
this.decoratedRun = decoratedRun;
this.w = w;
}
@Override
public void run() {
runner = Thread.currentThread();
try {
decoratedRun.run();
} finally {
dispose();
runner = null;
}
}
@Override
public void dispose() {
if (runner == Thread.currentThread() && w instanceof NewThreadWorker) {
((NewThreadWorker)w).shutdown();
} else {
w.dispose();
}
}
@Override
public boolean isDisposed() {
return w.isDisposed();
}
}
複製程式碼
DisposeTask 又是一個Runnable,同時也實現了Disposable介面。可以看到在他的run方法中會執行decoratedRun的run方法,這個decoratedRun其實就是引數中傳遞進來的run,也就是說,執行了這個DisposeTask的run方法,就會觸發SubscribeTask中的run方法,因此,我們就要關注是誰執行了這個DisposeTask。
回到scheduleDirect()方法
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
final Worker w = createWorker();
// 對run進行了一次裝飾
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
DisposeTask task = new DisposeTask(decoratedRun, w);
w.schedule(task, delay, unit);
return task;
}
複製程式碼
scheduleDirect()方法的實現我們總結一下:
- 建立一個Worker物件w,而在Scheduler類中createWorker()方法被定義為抽象方法,因此我們需要去Scheduler的具體實現中瞭解這個Worker的具體實現。
- 對引數run通過RxJavaPlugins進行一次裝飾,生成一個decoratedRun的Runnable(通過原始碼可以發現,其實什麼也沒幹,就是原樣返回)
- 通過decoratedRun和w生成一個DisposeTask物件task
- 通過Worker的schedule方法開始執行這個task。
ε=(´ο`*)))唉,說了這麼久,子執行緒是如何建立的依然不清楚,無論是SubscribeTask還是DisposeTask只是定義會在某個子執行緒中執行的任務,並不代表子執行緒已被建立。但是通過以上程式碼,我們也可以收穫一些有價值的結論:
- 最終的Runnable任務,將由某個具體的Worker物件的scheduler()方法執行。
- 這個scheduleDirect會返回一個Disposable物件,這樣我們就可以通過Observer去控制整個上游的執行了。
好了,到這裡對於subscribeOn()方法的分析已經到了盡頭,我們找了最終需要執行子任務的物件Worker,而這個Worker是個抽象類,因此我們需要關注Worker的具體實現了。
下面我們就從剛才丟下的Schedulers.newThread() 換個角度來分析,看看能不能找到這個Worker的具體實現。
Schedulers.newThread()
前面說了subscribeOn()方法需要一個Scheduler 型別的引數,然而通過前面的分析我們知道Scheduler是個抽象類,是無法被例項化的。因此,這裡就從Schedulers類出發。
/**
* Static factory methods for returning standard Scheduler instances.
*/
public final class Schedulers {
}
複製程式碼
註釋很清楚,這個Schedulers就是一個用於生成Scheduler例項的靜態工廠。
下面我們就來看看,在這個工廠中newThread() 生成了一個什麼樣的Scheduler例項。
@NonNull
public static Scheduler newThread() {
return RxJavaPlugins.onNewThreadScheduler(NEW_THREAD);
}
NEW_THREAD = RxJavaPlugins.initNewThreadScheduler(new NewThreadTask());
static final class NewThreadTask implements Callable<Scheduler> {
@Override
public Scheduler call() throws Exception {
return NewThreadHolder.DEFAULT;
}
}
static final class NewThreadHolder {
static final Scheduler DEFAULT = new NewThreadScheduler();
}
複製程式碼
newThread() 方法經過層層委託處理(最終的建立方式,有點單例模式的意味),最終我們需要的就是一個NewThreadScheduler的例項。
NewThreadScheduler.java
public final class NewThreadScheduler extends Scheduler {
final ThreadFactory threadFactory;
private static final String THREAD_NAME_PREFIX = "RxNewThreadScheduler";
private static final RxThreadFactory THREAD_FACTORY;
/** The name of the system property for setting the thread priority for this Scheduler. */
private static final String KEY_NEWTHREAD_PRIORITY = "rx2.newthread-priority";
static {
int priority = Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY,
Integer.getInteger(KEY_NEWTHREAD_PRIORITY, Thread.NORM_PRIORITY)));
THREAD_FACTORY = new RxThreadFactory(THREAD_NAME_PREFIX, priority);
}
public NewThreadScheduler() {
this(THREAD_FACTORY);
}
public NewThreadScheduler(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
@NonNull
@Override
public Worker createWorker() {
return new NewThreadWorker(threadFactory);
}
}
複製程式碼
不出所料NewThreadScheduler 是Scheduler的一個子類,在他的靜態程式碼塊中構造了一個Priority=5的執行緒工廠。而在我們最最關注的createWorker()方法中他又用這個執行緒工廠建立了一個NewThreadWorker 的例項。下面就讓我們看看最終的NewThreadWorker 做了些什麼工作。
NewThreadWorker.java(節選關鍵內容)
public class NewThreadWorker extends Scheduler.Worker implements Disposable {
private final ScheduledExecutorService executor;
volatile boolean disposed;
public NewThreadWorker(ThreadFactory threadFactory) {
executor = SchedulerPoolFactory.create(threadFactory);
}
@NonNull
@Override
public Disposable schedule(@NonNull final Runnable run) {
return schedule(run, 0, null);
}
@Override
public void dispose() {
if (!disposed) {
disposed = true;
executor.shutdownNow();
}
}
}
複製程式碼
眾裡尋他千百度,終於找到了Worker的實現了,同時再一次不出所料的又一次實現了Disposable介面,o(╥﹏╥)o。
在其建構函式中,通過NewThreadScheduler中提供的執行緒工廠threadFactory建立了一個ScheduledExecutorService。
ScheduledExecutorService.java ---create
public static ScheduledExecutorService create(ThreadFactory factory) {
final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);
if (PURGE_ENABLED && exec instanceof ScheduledThreadPoolExecutor) {
ScheduledThreadPoolExecutor e = (ScheduledThreadPoolExecutor) exec;
POOLS.put(e, exec);
}
return exec;
}
複製程式碼
用大名鼎鼎的Executors(Executor的工具類),建立了一個核心執行緒為1的執行緒。
至此,我們終於找到了第一個問題的答案,子執行緒是誰如何建立的;在NewThreadScheduler的createWorker()方法中,通過其構建好的執行緒工廠,在Worker實現類的建構函式中建立了一個ScheduledExecutorService的例項,是通過SchedulerPoolFactory建立的。
同時可以看到,通過執行dispose 方法,可以使用ScheduledExecutorService的shutdown()方法,停止執行緒的執行。
執行緒已經建立好了,下面就來看看到底是誰啟動了這個執行緒。前面我們說過,Worker的schedule()方法如果執行了,就會執行我們定義好的Runnable,通過這個Runnable中run方法的執行,就可以實現上下游訂閱關係。下面就來看看這個scheduler()方法。
@NonNull
@Override
public Disposable schedule(@NonNull final Runnable action, long delayTime, @NonNull TimeUnit unit) {
if (disposed) {
return EmptyDisposable.INSTANCE;
}
return scheduleActual(action, delayTime, unit, null);
}
@NonNull
public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);
if (parent != null) {
if (!parent.add(sr)) {
return sr;
}
}
Future<?> f;
try {
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;
}
複製程式碼
到這裡,已經很明顯了,在schedulerActual方法中,會通過剛才建立好的子執行緒物件executor通過submit或schedule執行一個Runnable任務(雖然這個Runnable物件再一次經過了各種裝飾和包裝,但其本質沒有發生變化),並將執行結果封裝後返回。而這個Runnable物件追根溯源來說,就是我們在ObservableSubscribeOn類中建立的一個SubscribeTask物件。因此,當這個子執行緒開始執行的時候就是執行SubscribeTask中run()方法的時機;一旦這個run方法執行,那麼
source.subscribe(parent)
複製程式碼
這句最關鍵的程式碼就開始執行了,一切的一切又回到了我們上一篇那熟悉的流程了。
好了,按照上面的流程捋下來,感覺還是有點分散,那麼就用UML圖看看整體的結構。
我們看最下面的ObservableSubscribeOn,他是subscribeOn 返回的Observable物件,他持有一個Scheduler 例項的引用,而這個Scheduler例項就是NewThreadScheduler(即Schedulers.newThreade())的一個例項。ObservableSubscribeOn 的subscribeActual方法,會觸發NewThreadScheduler去執行SubscribeTask中定義的任務,而這個具體的任務又將由Worker類建立的子執行緒去執行。這樣就把上游事件放到了一個子執行緒中實現。
至於最後一個問題,多次用 subscribeOn 指定上游執行緒為什麼只有第一次有效?,看完通篇其實也很好理解了,因為上游Observable只有一個任務,就是subscribe(準確的來說是subscribeActual()),而subscribeOn 要做的事情就是把上游任務切換到一個指定執行緒裡,那麼一旦被切換到了某個指定的執行緒裡,後面的切換不就是沒有意義了嗎。
好了,至此上游事件切換到子執行緒的過程我們就明白了。下游事件又是如何切換的且聽下回分解,本來想一篇寫完的,結果發現越寫越多,只能分成兩篇了!!!o(╯□╰)o。
寫在後面的話
關於Disposable
在RxJava的分析中,我們經常會遇到Disposable這個單詞,確切的說是介面,這裡簡單說一說這個介面。
/**
* Represents a disposable resource.
*/
public interface Disposable {
void dispose();
boolean isDisposed();
}
複製程式碼
我們知道,在Java中,類實現某個介面,通俗來說就是代表這個類多了一項功能,比如一個類實現Serializable介面,代表這個類是可以序列化的。這裡Disposable也是代表一種能力,這個能力就是Disposable,就是代表一次性的,用後就丟棄的,比如一次性筷子,還有那啥。
在RxJava中很多類都實現了這個介面,這個介面有兩個方法,isDisposed()顧名思義返回當前類是否被拋棄,dispose()就是主動拋棄。因此,所有實現了這個介面的類,都擁有了這樣一種能力,就是可以判斷自己是否被拋棄,同時也可以主動拋棄自己。
上一篇我們說了,Observer通過onSubscribe(@NonNull Disposable d),會獲得一個Disposable,這樣就有能力控制上游的事件傳送了。這樣,我們就不難理解,為什麼那麼多類實現了這個介面,因為下游獲取到的是一個擁有Disposable的物件,而一旦擁有了一個這樣的物件,那麼就可以通過下游控制上游了。可以說,這是RxJava對常規的觀察者模式所做的最給力的改變。
關於各種ObservableXXX ,subscribeXXX,ObserverXXX
在檢視RxJava的原始碼時,可能很多人都和我一樣,有一個巨大的困擾,就是這些類的名字好他媽難記,感覺長得都差不多,關鍵念起來好像也差不多。但其實本質上來說,RxJava對類的命名還是非常規範的,只是我們不太習慣而已。按照英文單詞翻譯:
- Observable 可觀察的
- Observer 觀察者
- Subscribe 訂閱
其實就這麼三個主語,其他的什麼ObservableCreate,ObservableSubscribeOn,AbstractObservableWithUpstream,還有上面提到的Disposable,都是對各種各樣的Observable和Observer的變形和修飾結果,只要理解這個類的核心含義是什麼,就不會被這些名字搞暈了。
RxJava 可以說是博大精深,以上所有分析完全是個人平時使用時的總結與感悟,有任何錯誤之處,還望各位讀者提出,共同進步。
關於RxJava 這裡牆裂推薦一篇文章一篇不太一樣的RxJava介紹,感覺是自扔物線那篇之後,對RxJava思想感悟最深的一篇了。對RxJava 有興趣的同學,可以多度幾遍,每次都會有收穫!!