前言
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的江湖越来越难保了