Rxjava(2.x版本)源碼解析——帶着問題讀源碼

前言

Rxjava已經過了頂峯期,現在已經很少有人願意去主動學習Rxjava了,(我所在的項目組,20多個人只有我會Rxjava),但是裏面的設計內容和思想我們還是不得不學的,rxjava的代碼設計其實不難,網上也有很多很優秀的講解RXjava的文章,我就不螳臂當車了,只是很多時候,分析完代碼,依然不知道可以學到什麼,所以我們這次,根據Rxjava的一些問題,來讀源碼。閱讀這篇文章,需要你有一定的rxjava基礎,也需要一些Rxjava源碼功底,至少知道鏈式調用是怎麼回事,所以,如果完全沒看過Rxjava,建議還是先去別的博客大概看下原理吧。
下面我們開始帶着問題來閱讀源碼。
再次強調,這是2.x的版本源碼分析,不是1.x的版本。

帶着問題讀源碼

問題一:Rxjava是怎麼實現線程切換的?

這是一個老生常談的問題,不管是誰,我相信剛學Rxjava的,也經常問自己這一點,下面我們來分析下。
java裏面所有的線程,說白了,都必須通過Thread的方式,不管是線程池的callable還是Future,本質都是Thread的run方法裏面封裝了一層有一層,Rxjava也不例外,所以,你需要明白,Rxjava的線程切換,只能是通過Runnable或者Callable封裝一層又一層,來實現線程的切換。(主線程的切換肯定是通過Runnable,因爲使用的Handler。)
題外話:本人在大學的時候最開始很討厭這種一層封裝又套一層的方式,直接實現Runnable的run方法不好嗎?後來工作了,看到了項目裏面一層套一層的Runnable,真的很煩,漸漸才慢慢喜歡這種方式,這種封裝利於閱讀,不會再一個又一個Runnable裏面反覆尋找來源和去處,現在自己寫代碼,也是儘量在Runnable上面封裝一層。
這裏只以ObserveOn爲例子,SubscribeOn會在第三個問題有分析。

ObserveOn源碼分析

使用ObserveOn,最後都會生成ObservableObserveOn。順帶多提一句,Rxjava可能什麼Observable和Observer容易看花眼,其實很簡單,如果是訂閱者,都是Observable+後面的描述符,如果是Observer,都是描述符+Observer,這樣也很符合我們寫代碼的順序。比如我們這樣寫。

Observable.just(true)
          .observeOn(Schedulers.io())
          .subscribe(new Consumer<Boolean>() {
              @Override
              public void accept(Boolean aBoolean) throws Exception {

              }
          });

observeOn生成的就是ObservableObserveOn和ObserveOnObserver。
回到正題,大家如過閱讀過Rx源碼,應該知道,所有上游核心功能都在subscribeActual中


    @Override
    protected void subscribeActual(Observer<? super T> observer) {
        if (scheduler instanceof TrampolineScheduler) {
            source.subscribe(observer);
        } else {
            Scheduler.Worker w = scheduler.createWorker();

            source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
        }
    }

這裏創建了Worker,Worker是Rxjava裏面實現線程的統一封裝類

Scheduler.Worker w = scheduler.createWorker();

可以看到,這裏只是把Worker傳入到ObserveOnObserver裏面,並沒有什麼實質性的操作(這是與SubscribeOn的主要區別),然後就是繼續回溯到source.subscribe(這裏我覺得使用遞歸的回溯來描述更準確,更清晰。)而這裏我們也可以大膽猜測,由上層執行到這裏的ObserveOnObserver時,肯定是由Worker實現線程切換的。
由於這是第一個問題,所以涉及到稍微全一點,Worker的代碼先不急,我們稍微說點前因,再聊後果。
我們知道,Rxjava核心是數據,並且都是通過onNext來發送數據的,那麼,發送的數據在那個線程呢,是先發送數據,再切換線程,還是先切換線程,再發送數據呢?(注意,這裏只是針對observeOn這個操作符號)
我們隨便找一個數據發射源,看下源碼

//ObservableFromArray類

        void run() {
            T[] a = array;
            int n = a.length;

            for (int i = 0; i < n && !isDisposed(); i++) {
                T value = a[i];
                if (value == null) {
                    downstream.onError(new NullPointerException("The " + i + "th element is null"));
                    return;
                }
                downstream.onNext(value);
            }
            if (!isDisposed()) {
                downstream.onComplete();
            }
        }

由此可見,在這裏並沒有切換線程的操作。
再來分析跟一下ObserveOnObserver源碼

//ObserveOnObserver類
 @Override
   public void onNext(T t) {
       if (done) {
           return;
       }

       if (sourceMode != QueueDisposable.ASYNC) {
           queue.offer(t);
       }
       schedule();
   }

看到這裏出現了schedule,由此可見線程切換的地方出現了。
這裏留一個疑問點,這個queue是幹什麼用的?以後有時間再來分析。簡單提示下:異步狀態下,存儲數據使用(聽說過背壓backpressure嗎?給個背壓參考博客關於RxJava最友好的文章——背壓(Backpressure))
接着看:

 void schedule() {
      if (getAndIncrement() == 0) {
          worker.schedule(this);
      }
  }

這裏其實我們也能猜到,裏面的流程肯定是封裝了切換線程的邏輯,然後執行run代碼
下面我們再看下worker的代碼(這裏我們以Android主線程的HandlerSchduler爲例子,來進行跟蹤)

	@Override
    public Worker createWorker() {
        return new HandlerWorker(handler, async);
    }

這裏返回的是HandlerWorker,結合之前的代碼,分析schedule

        @Override
        @SuppressLint("NewApi") // Async will only be true when the API is available to call.
        public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
            if (run == null) throw new NullPointerException("run == null");
            if (unit == null) throw new NullPointerException("unit == null");

            if (disposed) {
                return Disposables.disposed();
            }

            run = RxJavaPlugins.onSchedule(run);

            ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);

            Message message = Message.obtain(handler, scheduled);
            message.obj = this; // Used as token for batch disposal of this worker's runnables.

            if (async) {
                message.setAsynchronous(true);
            }

            handler.sendMessageDelayed(message, unit.toMillis(delay));

            // Re-check disposed state for removing in case we were racing a call to dispose().
            if (disposed) {
                handler.removeCallbacks(scheduled);
                return Disposables.disposed();
            }

            return scheduled;
        }

這裏可以看到通過handler發送Message到了主線程,這樣理解起來也就容易多了。(竟然還支持異步消息,也是看源碼我才發現)。

問題二:Rxjava是怎麼實現延遲的?

這個問題也曾經困擾着我,我一直在想,Rxjava的延遲,是不是和Handler.postDelay一樣?看了源碼發現,完全不是,先說結論:Rxjava使用線程池ScheduledThreadPoolExecutor來解決延時問題。
首先,最常見的delay使用源碼。

        Observable.just(true)
                .delay(3, TimeUnit.SECONDS) 

最到底,最後生成的都是ObservableDelay這個類,我們來看ObservableDelay


    @Override
    @SuppressWarnings("unchecked")
    public void subscribeActual(Observer<? super T> t) {
        Observer<T> observer;
        if (delayError) {
            observer = (Observer<T>)t;
        } else {
            observer = new SerializedObserver<T>(t);
        }

        Scheduler.Worker w = scheduler.createWorker();

        source.subscribe(new DelayObserver<T>(observer, delay, unit, w, delayError));
    }

這裏的w其實是EventLoopWorker,來簡單過一下生成流程,scheduler選擇默認的ComputationScheduler

//ComputationScheduler
    @NonNull
    @Override
    public Worker createWorker() {
        return new EventLoopWorker(pool.get().getEventLoop());
    }

這裏的pool又是什麼呢

final AtomicReference<FixedSchedulerPool> pool;

public PoolWorker getEventLoop() {
    int c = cores;
    if (c == 0) {
        return SHUTDOWN_WORKER;
    }
    // simple round robin, improvements to come
    return eventLoops[(int)(n++ % c)];
}

pool的初始化是在static代碼塊中

static {
        MAX_THREADS = cap(Runtime.getRuntime().availableProcessors(), Integer.getInteger(KEY_MAX_THREADS, 0));

        SHUTDOWN_WORKER = new PoolWorker(new RxThreadFactory("RxComputationShutdown"));
        SHUTDOWN_WORKER.dispose();

        int priority = Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY,
                Integer.getInteger(KEY_COMPUTATION_PRIORITY, Thread.NORM_PRIORITY)));

        THREAD_FACTORY = new RxThreadFactory(THREAD_NAME_PREFIX, priority, true);

        NONE = new FixedSchedulerPool(0, THREAD_FACTORY);
        NONE.shutdown();
    }

public ComputationScheduler(ThreadFactory threadFactory) {
    this.threadFactory = threadFactory;
    this.pool = new AtomicReference<FixedSchedulerPool>(NONE);
    start();
}

這裏都沒有什麼好說的,本來打算一筆帶過,就是初始化線程池相關操作,但是還是暫時貼出來,如果效果不好以後就去掉吧。
剩下的流程就和上面一樣,回溯到最高層,然後onNext,就這樣一直回溯即可,下面我們繼續看onNext函數。

@Override
 public void onNext(final T t) {
     w.schedule(new OnNext(t), delay, unit);
 }

然後就到了上面說的EventLoopWorker的schedule

@NonNull
@Override
public Disposable schedule(@NonNull Runnable action) {
    if (disposed) {
        return EmptyDisposable.INSTANCE;
    }
    return poolWorker.scheduleActual(action, 0, TimeUnit.MILLISECONDS, serial);
}

這裏的poolWorker就是上面返回的getEventLoop返回的PoolWorker,PoolWorker extends NewThreadWorker

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

這裏核心代碼就是執行schedule,executor就是ScheduledExecutorService
在這裏插入圖片描述
在這裏插入圖片描述
這裏都沒什麼好說的,到這裏應該就明白了.

小問題一:ScheduledThreadPoolExecutor如何實現延遲。

這個屬於java線程池的範疇了,先說結論:基於LockSupport.park()和unpark()實現。這兩個的實現,運用了操作系統底層的原理,具體大家可以自己查閱相關資料。

問題三:Rxjava是怎麼實現,SubscribeOn必須在ObserveOn之前調用才起作用的?

這個涉及到SubscribeOn源碼,其實也很簡單,這裏暫時先不分析,大家自己分析,晚點我再來更行一波博客。

後記

這個博客應該會不定期更新,添加一些新的問題,如果有時間,後面還可以採用自己定製Rxjava操作符的方式,一起簡單練練手。
Rxjava最讓人頭疼的就是太多的運算符,而且由於上手成本較高,很難在項目裏面全面推廣。還有,Rxjava默認的線程池是Cached,缺乏統一管理,這些都需要基於Rxjava源碼去定製。

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