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源码去定制。

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