“碼上理解”---RxJava 線程調度分析(一)切換子線程

RxJava簡單介紹

RxJava現在幾乎每個Android項目中都有它的身影,RxJava是使用了Rx(ReactiveX)函數庫的語言庫,Rx是一個函數庫,讓開發者可以利用可觀察序列和LINQ風格查詢操作符來編寫異步和基於事件的程序,除了RxJava,Rx幾乎支持了全部的流行編程語言,例如:RxJS、Rx.NET等等。

RxJava的優點和使用方法這篇文章不做介紹,今天關注一下框架內部原理:比如我們平時用RxJava的時候會使用subscribeOn(Schedulers.io())和observeOn(AndroidSchedulers.mainThread())來切換線程,有沒有對其內部的實現感興趣呢?
讀這篇文章之前可以先看看這篇,有助於理解流程關係 《碼上理解— 手撕RxJava訂閱關係,事件發送和接收》

文章目的

通過看源碼的實現過程,來理解RxJava在Android平臺上如何通過subscribeOn(Schedulers.io())方法實現線程調度的

環境搭建和代碼示例

示例是基於RxJava2
配置和代碼很簡單,如下

    implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
public class MainActivity extends AppCompatActivity {
    private Disposable disposable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rxJava();
    }

    public void rxJava() {
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                e.onNext("事件1");
            }
        })
                .subscribeOn(Schedulers.io())
                //.observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {

                    @Override
                    public void onSubscribe(Disposable d) {
                        Log.i("RxJava",s+","+Thread.currentThread().getName());
                        disposable = d;
                    }

                    @Override
                    public void onNext(String s) {
                        Log.i("RxJava",s);
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (disposable != null)
            if (!disposable.isDisposed())
                disposable.dispose();
    }
}

運行後日志打印

I/RxJava: 訂閱
I/RxJava: 事件1,RxCachedThreadScheduler-1

文章有點長,可以先拉到底部,先看總結,然後再一步一步跟代碼

分析subscribeOn(Schedulers.io())

我們先嚐試看一下subscribeOn(Schedulers.io())這個方法
點擊subscribe方法進入源碼

    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.CUSTOM)
    public final Observable<T> subscribeOn(Scheduler scheduler) {
        ObjectHelper.requireNonNull(scheduler, "scheduler is null");
        return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
    }

發現進入了抽象類Observable的subscribeOn方法,方法的參數是Scheduler 類型,Scheduler的翻譯是調度器,進一步跟蹤Scheduler類,發現Scheduler類也是抽象類,以下是部分代碼,可以先放上混個眼熟

public abstract class Scheduler {
    ... ...
    @NonNull
    public abstract Worker createWorker();
    ... ...
    @NonNull
    public Disposable scheduleDirect(@NonNull Runnable run) {
        return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
    }
 	... ...
 	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);
	}
	... ...
}

由於Schedulers.io()就是方法實參,所以我們推斷Schedulers.io()就是Scheduler的實現類,換句話說Schedulers.io()就是調度器,那我們先看以下這個調度器是什麼吧!

Schedulers.io()

我們嘗試點擊Schedulers類,跳轉源碼,發現用到了策略模式,Schedulers.io()是其中一個策略
常見的策略的使用場景瞭解一下

1.Schedulers.io(): I/O 操作(讀寫文件、讀寫數據庫、網絡信息交互等)所使用的 Scheduler。行爲模式和 newThread() 差不多,區別在於 io() 的內部實現是是用一個無數量上限的線程池,可以重用空閒的線程,因此多數情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中,可以避免創建不必要的線程。
2.Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算,即不會被 I/O 等操作限制性能的操作,例如圖形的計算。這個 Scheduler 使用的固定的線程池,大小爲 CPU 核數。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU。
3.另外, Android 還有一個專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行。

我們看一下Schedulers.io()具體的實現方式

	@NonNull
    public static Scheduler io() {
        return RxJavaPlugins.onIoScheduler(IO);
    }

發現返回了RxJavaPlugins.onIoScheduler,這個類RxJavaPlugins下的方法其實是實現hook,由於我們當前不關心hook,先不用管它,它方法內返回的其實就是IO對象

 /**
     * Calls the associated hook function.
     * @param defaultScheduler the hook's input value
     * @return the value returned by the hook
     */
    @NonNull
    public static Scheduler onIoScheduler(@NonNull Scheduler defaultScheduler) {
        Function<? super Scheduler, ? extends Scheduler> f = onIoHandler;
        if (f == null) {
            return defaultScheduler;
        }
        return apply(f, defaultScheduler);
    }

ok,現在我們看一下IO是哪來的,發現其實在類中的靜態代碼塊中初始化的

 static {
        SINGLE = RxJavaPlugins.initSingleScheduler(new SingleTask());

        COMPUTATION = RxJavaPlugins.initComputationScheduler(new ComputationTask());
		//這裏初始化了調度器IO
        IO = RxJavaPlugins.initIoScheduler(new IOTask());

        TRAMPOLINE = TrampolineScheduler.instance();

        NEW_THREAD = RxJavaPlugins.initNewThreadScheduler(new NewThreadTask());
    }

又看到了RxJavaPlugins類,我們先不去管它,直接看new IOTask()

    static final class IOTask implements Callable<Scheduler> {
        @Override
        public Scheduler call() throws Exception {
            return IoHolder.DEFAULT;
        }
    }

發現他是一個任務,返回值是Scheduler 調度器,那它是從哪裏執行這個任務的呢,我們這時可以看RxJavaPlugins.initNewThreadScheduler這個方法了

 @NonNull
    public static Scheduler initIoScheduler(@NonNull Callable<Scheduler> defaultScheduler) {
        ObjectHelper.requireNonNull(defaultScheduler, "Scheduler Callable can't be null");
        //代碼中如果沒有實現onInitIoHandler,這裏始終是null的
        Function<? super Callable<Scheduler>, ? extends Scheduler> f = onInitIoHandler;
        if (f == null) {
            return callRequireNonNull(defaultScheduler);
        }
        return applyRequireNonNull(f, defaultScheduler);
    }

我們傳入了Callable,返回的是Scheduler ,說明在這個方法中執行了IOTask 任務,於是我們進一步看方法callRequireNonNull(defaultScheduler)。
這裏還要說明一下,如果我們沒有自己實現hook方法setInitIoSchedulerHandler(), onInitIoHandler這裏始終是null,所以我們直接看一個參數的方法callRequireNonNull

@NonNull
    static Scheduler callRequireNonNull(@NonNull Callable<Scheduler> s) {
        try {
            return ObjectHelper.requireNonNull(s.call(), "Scheduler Callable result can't be null");
        } catch (Throwable ex) {
            throw ExceptionHelper.wrapOrThrow(ex);
        }
    }

ok,我們發現s.call(),執行了IOTask任務。我們再回到IOTask,我們看看到底返回的調度器是個什麼東東

    static final class IoHolder {
        static final Scheduler DEFAULT = new IoScheduler();
    }

發現IoHolder 其實是Schedulers的靜態內部類,靜態內部類中又創建了靜態對象IoScheduler,看一下
IoScheduler

/**
 * Scheduler that creates and caches a set of thread pools and reuses them if possible.
 */
public final class IoScheduler extends Scheduler {
static {
       	...
       	//工作線程的線程工廠
       	WORKER_THREAD_FACTORY = new RxThreadFactory(WORKER_THREAD_NAME_PREFIX, priority);
       	//線程池
        NONE = new CachedWorkerPool(0, null, WORKER_THREAD_FACTORY);
        NONE.shutdown();
    }
	//無參構造
    public IoScheduler() {
        this(WORKER_THREAD_FACTORY);
    }
    //傳入了用於創建工作線程的線程工廠
    public IoScheduler(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
        this.pool = new AtomicReference<CachedWorkerPool>(NONE);
        start();
    }

    public IoScheduler() {
        this(WORKER_THREAD_FACTORY);
    }
    
    // 實現了 Scheduler 的createWorker方法
    @NonNull
    @Override
    public Worker createWorker() {
        return new EventLoopWorker(pool.get());
    }
	...
	//實現了Scheduler.Worker 抽象類
    static final class EventLoopWorker extends Scheduler.Worker {
		...
		@NonNull
        @Override
        public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
            if (tasks.isDisposed()) {
                // don't schedule, we are unsubscribed
                return EmptyDisposable.INSTANCE;
            }

            return threadWorker.scheduleActual(action, delayTime, unit, tasks);
        }
        ...
	}
}

上面的代碼我們可以看出,調度器裏其實是創建了線程池,所以,此處可以推測.subscribeOn(Schedulers.io())方法將每一個事件當作任務放入線程池執行,從而達到線程血環的目的。
再注意一點,IO調度器IoScheduler類中實現了一個抽象方法,一個抽象類,還記得文章一開始混眼熟的那個類嗎,ok,我們再眼熟一下因爲一會要用
在這裏插入圖片描述
到目前爲止,我們知道Schedulers.io()其實就是創建了IoScheduler,它的本質是一個線程池,由方法subscribeOn(Schedulers.io()),IoScheduler對象丟給了subscribeOn方法,那我們進一步看一下subscribeOn方法中究竟是賣的什麼藥

subscribeOn()

subscribeOn我們接着看

    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.CUSTOM)
    public final Observable<T> subscribeOn(Scheduler scheduler) {
        ObjectHelper.requireNonNull(scheduler, "scheduler is null");
        return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
    }

前面如果都跟者看過來的話,我們就可以直接進入ObservableSubscribeOn類了(RxJavaPlugins.onAssembly()方法返回的就是ObservableSubscribeOn對象)
ObservableSubscribeOn類我全粘過來了,代碼比較長

public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
    final Scheduler scheduler;//IoScheduler

    public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
        super(source);
        this.scheduler = scheduler;
    }

    @Override
    public void subscribeActual(final Observer<? super T> s) {
    	//包裝一層,將觀察者Observer放入SubscribeOnObserver對象中
        final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
		//調用觀察者Observer的onSubscribe方法
        s.onSubscribe(parent);

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

    static final class SubscribeOnObserver<T> extends AtomicReference<Disposable> implements Observer<T>, Disposable {

        private static final long serialVersionUID = 8094547886072529208L;
        final Observer<? super T> actual;

        final AtomicReference<Disposable> s;

        SubscribeOnObserver(Observer<? super T> actual) {
            this.actual = actual;
            this.s = new AtomicReference<Disposable>();
        }

        @Override
        public void onSubscribe(Disposable s) {
            DisposableHelper.setOnce(this.s, s);
        }

        @Override
        public void onNext(T t) {
            actual.onNext(t);
        }

        @Override
        public void onError(Throwable t) {
            actual.onError(t);
        }

        @Override
        public void onComplete() {
            actual.onComplete();
        }

        @Override
        public void dispose() {
            DisposableHelper.dispose(s);
            DisposableHelper.dispose(this);
        }

        @Override
        public boolean isDisposed() {
            return DisposableHelper.isDisposed(get());
        }

        void setDisposable(Disposable d) {
            DisposableHelper.setOnce(this, d);
        }
    }

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

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

        @Override
        public void run() {
            source.subscribe(parent);
        }
    }
}

拿到這個類,我們可以知道ObservableSubscribeOn對象中有傳入的IoScheduler調度器,有一個subscribeActual方法,有一個SubscribeOnObserver靜態內部類,還有一個訂閱任務SubscribeTask。
如果看過我另一篇文章的碼上理解— 手撕RxJava訂閱關係,事件發送和接收
應該知道,subscribeActual這個方法其實是由下游subscribe方法調用,也就是執行subscribe就執行subscribeActual
那重點就是subscribeActual方法,看一下subscribeActual方法幹了些什麼

    @Override
    public void subscribeActual(final Observer<? super T> s) {
    	//包裝一層,將觀察者Observer放入SubscribeOnObserver對象中
        final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
		//調用觀察者Observer的onSubscribe方法
        s.onSubscribe(parent);
		//最後創建了一個任務SubscribeTask,把任務丟給IoScheduler調度器
        parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
    }

先把下游的Observer對象包裝進SubscribeOnObserver對象,然後執行了observer的onSubscribe方法,表示訂閱完成,最後創建了一個任務SubscribeTask,把任務丟給IoScheduler調度器
那IoScheduler的scheduleDirect方法具體幹了些什麼呢,還記得文章開頭混臉熟的Scheduler類嗎?
在這裏插入圖片描述
scheduler.scheduleDirect實際上就是調用到這裏了,我們再看一下三個參數的scheduleDirect方法

public abstract class Scheduler {
	@NonNull
	    public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
	        //還記得IoScheduler 的createWorker方法嗎
	        final Worker w = createWorker();
			// SubscribeTask
	        final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
			// 給SubscribeTask包裝一層DisposeTask,實現了Disposable接口
	        DisposeTask task = new DisposeTask(decoratedRun, w);
			// 執行 EventLoopWorker的schedule方法
	        w.schedule(task, delay, unit);
	
	        return task;
	    }
}

上面註釋已經解釋了,scheduleDirect方法就是將SubscribeTask丟給了schedule方法,那看一下schedule方法具體幹了啥
在這裏插入圖片描述
在這裏插入圖片描述
方法中線程池executor執行了SubscribeTask任務,我們再看一下SubscribeTask的run方法中執行的是什麼

public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
    final Scheduler scheduler;

    public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
        super(source);
        this.scheduler = scheduler;
    }
    ...
    ...
    final class SubscribeTask implements Runnable {
        private final SubscribeOnObserver<T> parent;

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

        @Override
        public void run() {
        	//執行了上游的subscribe方法
            source.subscribe(parent);
        }
    }
}

run方法中調用了source的subscribe方法。source是SubscribeOnObserver構造方法傳入進來的,它其實就是Observable
在這裏插入圖片描述
那source.subscribe就是調用的Observable的subscribe方法,ok,找一下subscribe
在這裏插入圖片描述
如果看過我另一篇文章的碼上理解— 手撕RxJava訂閱關係,事件發送和接收的老鐵應該知道,subscribeActual是抽象方法,subscribeActual(observer)是跳轉到哪裏,沒錯是Observable.create創建出的ObservableCreate對象裏的subscribeActual方法。
還記得subscribeActual(observer)的參數observer是什麼嗎,是被SubscribeOnObserver包裝的觀察者Observer,來看一下ObservableCreate對象中的subscribeActual方法做了什麼
在這裏插入圖片描述
在這裏插入圖片描述
上圖中爲什麼說e.onNext方法執行在子線程中呢?

因爲下游執行subscribe方法的時候,
1.先將Observer觀察者包裝到SubscribeOnObserver中去(裝飾模式)
2.然後執行了Observer的onSubscribe回調,訂閱完成
3.創建一個任務SubscribeTask,將裝有觀察者Observer的包裹SubscribeOnObserver傳給這個任務,方便在run方法中將包裹SubscribeOnObserver傳給上游,上游拿到包裹可以再進行添加包裹或者解開包裹執行Observer的onNext回調或其它回調
4.將任務SubscribeTask丟進IO調度器IoScheduler中的線程池並submit執行
5.SubscribeTask中的 source.subscribe(parent);將包裹SubscribeOnObserver傳給上游,由於此時已經運行在線程池中了,所以上游的subscribeActual方法是運行在子線程中,ObservableOnSubscribe的subscribe回調是在subscribeActual方法中調用的,所以subscribe方法也是運行在子線程,e.onNext在方法subscribe裏調用,所以e.onNext方法執行也在子線程中。沒毛病。

總結

RxJava通過subscribeOn(Schedulers.io())切換子線程的原理是將subscribeOn的上游都放入線程池中執行;從這裏也可以推斷出另一個結論,當有多個subscribeOn的時候,只有上游的第一個subscribeOn有效,換句話說程序會運行在上游第一個subscribeOn()所切換的線程裏。
最後來張圖總結一下
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章