Rxjava 2.x 原始碼系列 - 執行緒切換 (上)

stormjun發表於2019-04-26

Rxjava 2.x 原始碼系列 - 基礎框架分析

Rxjava 2.x 原始碼系列 - 執行緒切換 (上)

Rxjava 2.x 原始碼系列 - 執行緒切換 (下)

Rxjava 2.x 原始碼系列 - 變換操作符 Map(上)

前言

在上一篇部落格 Rxjava 原始碼系列 - 基礎框架分析,我們分析了 Rxjava 的基礎框架。

Observable 和 Observer 通過 subscribe() 方法實現訂閱關係,從而 Observable 可以在需要的時候發出事件來通知 Observer,並且回撥 Observer 的相應的方法。

用一張簡單的流程圖描述如下:

image


Observable#subscribeOn

在 Android 中,我們知道預設都是執行在主執行緒的,那麼 Rxjava 是如何實現執行緒切換的。

Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                emitter.onNext("1");
                emitter.onNext("2");
                emitter.onNext("3");
                emitter.onComplete();
            }
        })
        .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.e("TAG", "onSubscribe():  ");
            }

            @Override
            public void onNext(String s) {
                Log.e("TAG", "onNext():  " + s);
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {
                Log.e("TAG", "onComplete():  ");
            }
        });
複製程式碼

我們先來看一下 subscribeOn 方法,可以看到

@CheckReturnValue
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable<T> subscribeOn(Scheduler scheduler) {
    // scheduler 判空
    ObjectHelper.requireNonNull(scheduler, "scheduler is null");
    // 用 ObservableSubscribeOn 將 scheduler 包裝 起來
    return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
}


複製程式碼

而我們從上一篇部落格中知道,當我們呼叫 observable.subscibe(observable) 的時候,最終會呼叫到具體的 observable 的例項的 subscribActual 方法。而這裡具體的 observable 的例項為 ObservableSubscribeOn。

接下來,我們來看一下 ObservableSubscribeOn 這個類,可以看到繼承 AbstractObservableWithUpstream ,而 AbstractObservableWithUpstream 繼承 Observable,實現 HasUpstreamObservableSource 這個介面。

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);

        s.onSubscribe(parent);

        parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
    }
    
    ---
}


abstract class AbstractObservableWithUpstream<T, U> extends Observable<U> implements HasUpstreamObservableSource<T> {

    /** The source consumable Observable. */
    protected final ObservableSource<T> source;

    /**
     * Constructs the ObservableSource with the given consumable.
     * @param source the consumable Observable
     */
    AbstractObservableWithUpstream(ObservableSource<T> source) {
        this.source = source;
    }

    @Override
    public final ObservableSource<T> source() {
        return source;
    }

}

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 的 subscribeActual 方法,跟 ObservableCreate 的 subscribeActual 的套路差不多,它也是 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);

        s.onSubscribe(parent);

        parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
    }
}
複製程式碼

首先先來看他的建構函式 ,有兩個引數 source ,scheduler。

  • source 代表上游的引用,是 Observable 的一個例項
  • scheduler 可以通過 Schedulers.newThread() 或者 Schedulers.io() 建立相應的例項

這裡我們先大概瞭解一下 Scheduler 是個什麼東東,Scheduler 裡面封裝了 Worker 和 DisposeTask,下面會詳細講到。

Schedulers.newThread()

@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();
}


複製程式碼
public static Scheduler io() {
    return RxJavaPlugins.onIoScheduler(IO);
}

IO = RxJavaPlugins.initIoScheduler(new IOTask());

static final class IOTask implements Callable<Scheduler> {
    @Override
    public Scheduler call() throws Exception {
        return IoHolder.DEFAULT;
    }
}
static final class IoHolder {
    static final Scheduler DEFAULT = new IoScheduler();
}
static final class IoHolder {
    static final Scheduler DEFAULT = new IoScheduler();
}
複製程式碼

我們再回到 ObservableSubscribeOn 的 subscribeActual 方法,在上一篇部落格的時候已經講解 Observable 和 Observer 之間是怎樣實現訂閱關係的,這裡就不再具體展開了。

接下來,我們重點關注這一行程式碼

 parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
 
複製程式碼

我們先來看一下 SubscribeTask 這個類,他是 ObservableSubscribeOn 的一個非靜態內部類,可以看到 其實也比較簡單,他實現了 Runnable 介面,並且持有 parent 引用。

final class SubscribeTask implements Runnable {
    private final SubscribeOnObserver<T> parent;

    SubscribeTask(SubscribeOnObserver<T> parent) {
        this.parent = parent;
    }

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

然後在 run 方法中,通過 source.subscribe(parent) 建立聯絡。因而,當我們的 SubscribeTask 的 run 方法執行在哪個執行緒,相應的 observer 的 subscribe 方法就執行在哪個執行緒。

這裡可能會有人有疑問,SubscribeTask 沒有 source 屬性,它是怎麼訪問到 ObservableSubscribeOn 的屬性的。

我們知道 java 中,非靜態內部類預設持有外部類的引用,因而他可以正常訪問外部類 ObservableSubscribeOn 的 source 屬性。


接著,我們再來看一下 scheduler.scheduleDirect 這個方法

@NonNull
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
    final Worker w = createWorker();

    // 判斷 run 是否為 null
    final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);

    DisposeTask task = new DisposeTask(decoratedRun, w);

    w.schedule(task, delay, unit);

    return task;
}


複製程式碼
  • 首先,建立一個 Worker w
  • 第二步,DisposeTask 將 decoratedRun 包裝起來
  • 第三步:w 去排程 task

這裡我們以 NewThreadScheduler 為例,來看看這個 Worker 到底是什麼?

public Worker createWorker() {
    return new NewThreadWorker(threadFactory);
}



public class NewThreadWorker extends Scheduler.Worker implements Disposable {
    private final ScheduledExecutorService executor;

    volatile boolean disposed;

    public NewThreadWorker(ThreadFactory threadFactory) {
        executor = SchedulerPoolFactory.create(threadFactory);
    }
    
    --- 
}


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;
}



複製程式碼

從上面可以看到,其實 worker 裡面封裝了 executor(執行緒池),看到這裡,相信你也基本明白 Rxjava 執行緒切換的原理了,其實很簡單。

在 ObservableSubscribeOn subscribeActual 方法中, SubscribeTask 包裝 parent(SubscribeOnObserver ,包裝了 Observer),SubscribeTask 實現了 Runnable 介面,在 run 方法裡面呼叫了 source.subscribe(parent),因而 run 方法所執行的執行緒將由 worker 決定。這就是 下游決定上游 observable 執行執行緒的原理。

接下來我們再來看一下:DisposeTask

static final class DisposeTask implements Disposable, Runnable, SchedulerRunnableIntrospection {
        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();
        }

        @Override
        public Runnable getWrappedRunnable() {
            return this.decoratedRun;
        }
    }
}
複製程式碼
// 將 新的 Disposable 設定給 parent ,方便取消訂閱關係,
//(因為我們對  Observer 進行相應的包裝,原來的 parent 的 Disposable 已經不能代表最新的 Disposable)
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
複製程式碼

DisposeTask 實現了 Disposable,Runnable ,SchedulerRunnableIntrospection 介面,Disposable 介面主要是用來取消訂閱關係的 Disposable。


Observable#subscribeOn(Scheduler) 第一次有效原理

Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                Log.i(TAG, "subscribe: getName=" +Thread.currentThread().getName());
                emitter.onNext("1");
                emitter.onNext("2");
                emitter.onNext("3");
                emitter.onComplete();
            }
        }) // 進行兩次 subscribeOn
        .subscribeOn(Schedulers.io()).subscribeOn(Schedulers.computation()).subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.e("TAG", "onSubscribe():  ");
            }

            @Override
            public void onNext(String s) {
                Log.e("TAG", "onNext():  " + s);
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {
                Log.e("TAG", "onComplete():  ");
            }
        });
複製程式碼

subscribe: getName=RxCachedThreadScheduler-1

如果將上述的 subscribeOn 的順序置換

subscribeOn(Schedulers.computation()).subscribeOn(Schedulers.io())
複製程式碼

那麼將列印出

subscribe: getName=RxComputationThreadPool-1

為什麼是第一次 Observable#subscribeOn(Scheduler) 才有效呢?

前面我們分析到,Observable#subscribeOn(Scheduler) 實際上是將 Observable#subscribe(Observer) 的操作放在了指定執行緒,當我們呼叫 subcribe 的時候,它的過程是從下往上的,即下面的 Observable 呼叫上面的 Observanle。

所以對於我們上面的第一個例子,他的呼叫流程是這樣的:第三個 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();
複製程式碼

總結

用流程圖描述如下:

Rxjava 2.x 原始碼系列 - 執行緒切換 (上)


參考部落格:

友好 RxJava2.x 原始碼解析(二)執行緒切換

下一篇我們將講解到 observeOn(AndroidSchedulers.mainThread()) 的原理。

Android 技術人

掃一掃,歡迎關注我的公眾號。如果你有好的文章,也歡迎你的投稿。

相關文章