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只有第一次调用有作用的之前调用才起作用的?

这个涉及到SubscribeOn源码,其实也很简单,这里暂时先不分析,大家自己分析,晚点我再来更行一波博客。
2020年8月30日。
等的时间有点长了,下面继续更新。
咱们看下SubscribeOn的源码,根据我们上面说的规律,subscribeOn的封装类肯定是叫ObservableSubscribeOn,而observer肯定是叫做subscribeActual

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

主要看scheduler.scheduleDirect()

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

        final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);

        DisposeTask task = new DisposeTask(decoratedRun, w);

        w.schedule(task, delay, unit);

        return task;
    }

这里的w有很多可选项,我们就以最简单的IoScheduler的EventLoopWorker为例

        @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;
            }
//这里的threadWorker是NewThreadWorker
            return threadWorker.scheduleActual(action, delayTime, unit, tasks);
        }
   /**
     * Wraps the given runnable into a ScheduledRunnable and schedules it
     * on the underlying ScheduledExecutorService.
     * <p>If the schedule has been rejected, the ScheduledRunnable.wasScheduled will return
     * false.
     * @param run the runnable instance
     * @param delayTime the time to delay the execution
     * @param unit the time unit
     * @param parent the optional tracker parent to add the created ScheduledRunnable instance to before it gets scheduled
     * @return the ScheduledRunnable instance
     */
    @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;
    }

这里源码跟着看完,就能明白,执行subscribeOn直接就切换线程了,而多次调用subscribeOn,就是切换了多次线程,最终只有第一次有用。

问题四:Rxjava是怎么实现,SubscribeOn的作用域在observeOn之前的?

这个问题其实跟上一个问题一样,明白了上一个问题,这个问题也就不难了。SubscribeOn是直接切换线程,而observeOn,是在执行到这里的时候,才切换线程,所以,SubscribeOn只有在observeOn之前才有用。

后记

这个博客应该会不定期更新,添加一些新的问题,如果有时间,后面还可以采用自己定制Rxjava操作符的方式,一起简单练练手。
Rxjava最让人头疼的就是太多的运算符,而且由于上手成本较高,很难在项目里面全面推广。还有,Rxjava默认的线程池是Cached,我们可以通过如下函数来传入自己的线程池

Schedulers.from(new Executor() {
    @Override
    public void execute(@NonNull Runnable runnable) {
        new Thread(runnable, "zyy-Thread").start();
    }
})

由于项目里面几乎没有人使用rxjava,并且,由于livedata和kotlin中协程的兴起,rxjava的江湖越来越难保了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值