RxJava 的线程调度实现原理
一、概述
RxJava 是一个响应式编程框架,里面代码比较复杂,本系列文章将从以下几个角度来分析这个框架。
- RxJava 的链路调用流程。
- RxJava 的常用操作符
map、flatmap
。 - RxJava 的线程调度。
- 自己实现一个简易版的 RxJava 框架。
在前两篇文章中,我们分析了 RxJava 的链路调用流程 和 常用的操作符 (
map、flatmap
) 的逻辑,本文我们来分析一下 Rxjava 线程调度的实现原理。在 Rxjava 中也对应两个操作符:subscribeOn()
、observeOn()
。
本文我们就来分析一下这两个操作符的原理。
版本:
Gradle 依赖:
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.19'
关联文章:
- RxJava(一) — 链路调用流程分析
- RxJava(二) — 常用操作符之 map & flatmap
- RxJava(三) — 线程调度
- RxJava(四) — 实现一个简易版的 RxJava 框架
- 设计模式 — 观察者模式
- RxJava 中文文档
二、线程调度的原理
所谓的线程调度,即让特定处理逻辑运行在我们指定的线程当中。Rxjava 为我们提供了多种线程池来处理特定逻辑,主要可以分为 IO 密集型线程池
,CPU密集型线程池
等多种线程池。当然更新 UI 只能在主线程中进行,因此还提供了一种将线程切换到主线程的功能。
线程调度的两种场景:
- 非主线程之间的调度: 即让我们指定的处理逻辑运行在 Runnable.run() 方法当中,并将这个 Runnable 提交给指定的线程池进行处理。
- 子线程切换到主线程: 在 Android 中,要从子线程切换到主线程,就要使用 Handler 进行切换。
三、线程调度操作符
有两个操作符可以执行线程调度:subscribeOn()
、observeOn()
,下面先来分析一下这两个操作符的实现原理。
1. 操作符 subscribeOn()
ObservableSubscribeOn
public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
final Scheduler scheduler;
public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
super(source);
// 1.此处的调度器就是用户传入的。
this.scheduler = scheduler;
}
@Override
public void subscribeActual(final Observer<? super T> observer) {
// 2.将接收ObservableSubscribeOn消息的Observer包装到SubscribeOnObserver中。
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(observer);
observer.onSubscribe(parent);
// 3.将Runnable类型的SubscribeTask通过scheduler.scheduleDirect()提交到内部的线程池去执行。
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
static final class SubscribeOnObserver<T> extends AtomicReference<Disposable> implements Observer<T>, Disposable {
final Observer<? super T> downstream;
SubscribeOnObserver(Observer<? super T> downstream) {
this.downstream = downstream;
this.upstream = new AtomicReference<Disposable>();
}
@Override
public void onNext(T t) {
// 5.onNext()方法将执行在执行SubscribeTask任务的线程当中。
downstream.onNext(t);
}
}
final class SubscribeTask implements Runnable {
private final SubscribeOnObserver<T> parent;
SubscribeTask(SubscribeOnObserver<T> parent) {
this.parent = parent;
}
@Override
public void run() {
// 4.核心:线程池会执行run方法,在run方法中执行逆向逐级订阅流程 `source.subscribe()`。
// 之后的订阅流程就会运行在执行run()方法的线程当中。
source.subscribe(parent);
}
}
}
小结: 这里理解以下 2 点即可:
在执行逆向逐级订阅流程时,调度器会将上个 Observable 的消息接收者 (
SubscribeOnObserver
) 传入到一个自定义的 Runnable (SubscribeTask
) 中,并将该任务提交给调度器(Scheduler
)执行。
注: 调度器内含有一个Worker
对象, Worker 对象内持有一个线程池,所以该 Runnable 任务最终是提交到线程池中执行的。在自定义的 Runnable (
SubscribeTask
) 中执行事件的订阅操作source.subscribe(parent)
,因此只要后续没有线程的切换,那么后续的逆向逐级订阅流程和执行任务链的流程都将在当前这个调度线程中执行。
2. 操作符 observeOn()
public final class ObservableObserveOn<T> extends AbstractObservableWithUpstream<T, T> {
final Scheduler scheduler;
public ObservableObserveOn(ObservableSource<T> source,
Scheduler scheduler, boolean delayError, int bufferSize) {
super(source);
// 1.此处的调度器就是用户传入的。
this.scheduler = scheduler;
// ...省略代码...
}
@Override
protected void subscribeActual(Observer<? super T> observer) {
if (scheduler instanceof TrampolineScheduler) {
source.subscribe(observer);
} else {
// 2.从调度器中获取一个处理任务的 Worker,这个在ObservableSubscribeOn小结中讲过。
Scheduler.Worker w = scheduler.createWorker();
// 3.将Observable与Observer进行关联,并将 Worker作为参数传入Observer。
source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
}
}
static final class ObserveOnObserver<T> extends BasicIntQueueDisposable<T>
implements Observer<T>, Runnable {
final Observer<? super T> downstream;
final Scheduler.Worker worker;
ObserveOnObserver(Observer<? super T> actual, Scheduler.Worker worker,
boolean delayError, int bufferSize) {
this.downstream = actual;
this.worker = worker;
// ...省略代码...
}
@Override
public void onNext(T t) {
if (done) {
return;
}
if (sourceMode != QueueDisposable.ASYNC) {
queue.offer(t);
}
// 3.在执行逆向逐级订阅流程时,触发此操作,进行线程切换。
schedule();
}
void schedule() {
if (getAndIncrement() == 0) {
// 4.注意ObserveOnObserver实现了Runnable接口,因此最终会执行到ObserveOnObserver.run()方法。
// 该方法处于执行任务链流程,所以如果后续没有线程调度,则接下去的逻辑都执行在当前的线程中。
worker.schedule(this);
}
}
@Override
public void run() {
if (outputFused) {
drainFused();
} else {
// 5.走常规处理流程
drainNormal();
}
}
void drainNormal() {
final SimpleQueue<T> q = queue;
// 6. downstream指向接收ObservableObserveOn事件的接收者。
final Observer<? super T> a = downstream;
for (;;) {
// ...省略代码...
for (;;) {
T v = q.poll();
// ...省略代码...
a.onNext(v); // 7.接收事件
}
// ...省略代码...
}
}
}
}
小结:
observeOn()
方法执行到线程调度在执行任务链流程中生效,会将之后的执行任务链流程运行在当前的线程中。
3. 示例代码分析
前面分析了 subscribeOn()
、observeOn()
两个操作符线程调度的原理,下面我们分析两个示例代码来加深理解。
Client 示例代码1:
Observable.just("11", "22", "33")
.map(new Function<String, Integer>() {
@Override
public Integer apply(String s) throws Exception {
return Integer.parseInt(s);
}
})
.subscribeOn(Schedulers.computation()) //指定map操作符运行在CPU密集型的线程池中。
.observeOn(AndroidSchedulers.mainThread()) //指定Observer在主线程进行接收。
.subscribe(new Observer<Integer>() {
//略
});
线程切换流程图如下:
小结:
- 根据上面的分析我们知道,调用了
subscribeOn()
方法,将线程从主线程切换到了计算线程。 - 调用
observeOn()
方法,将线程从计算线程切换到了主线程。 map
操作符的mapper.apply()
方法执行在计算线程中。
Client 示例代码2:
Observable.just(1, 2, 3, 4) // IO 线程,由 subscribeOn() 指定
.subscribeOn(Schedulers.io())
.map(mapOperator) // IO 线程,由 subscribeOn() 指定
.observeOn(Schedulers.computation())
.map(mapOperator2) // 新线程,由 observeOn() 指定
.subscribeOn(Schedulers.newThread())
.map(mapOperator3) // 新线程,由 observeOn() 指定
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber); // Android 主线程,由 observeOn() 指定
线程切换流程图如下:
四、其它线程切换到主线程的流程分析
将线程切换到主线程会使用到 AndroidSchedulers.mainThread()
调度器,里面其实是一个 HandlerScheduler
调度器。
我们知道,在 ObservableObserveOn 中,最终会执行 worker.schedule(Runnable)
方法,这个 worker 是由 scheduler.createWorker()
创建的。
下面我们来分析一下 HandlerScheduler 的代码实现。
HandlerScheduler
final class HandlerScheduler extends Scheduler {
private final Handler handler;
private final boolean async;
HandlerScheduler(Handler handler, boolean async) {
this.handler = handler;
this.async = async;
}
@Override
@SuppressLint("NewApi") // Async will only be true when the API is available to call.
public Disposable scheduleDirect(Runnable run, long delay, TimeUnit unit) {
if (run == null) throw new NullPointerException("run == null");
if (unit == null) throw new NullPointerException("unit == null");
run = RxJavaPlugins.onSchedule(run);
ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);
Message message = Message.obtain(handler, scheduled);
if (async) {
message.setAsynchronous(true);
}
handler.sendMessageDelayed(message, unit.toMillis(delay));
return scheduled;
}
@Override
public Worker createWorker() {
// 1.用于处理 Runnable 的Worker。
return new HandlerWorker(handler, async);
}
private static final class HandlerWorker extends Worker {
private final Handler handler;
private final boolean async;
private volatile boolean disposed;
HandlerWorker(Handler handler, boolean async) {
this.handler = handler;
this.async = async;
}
@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);
// 2.构建一个Runnable对象,里面包含了将线程进行切换的Handler和外部传入的Runnable。
ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);
// 3.将该 Runnable 对象封装到 Msg.callback 字段中,这个Runnable会在Handler.dispatchMessage()中被执行。
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);
}
// 4.将该消息发送到主线程中接收,于是就实现了线程的切换。
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;
}
}
private static final class ScheduledRunnable implements Runnable, Disposable {
ScheduledRunnable(Handler handler, Runnable delegate) {
this.handler = handler;
this.delegate = delegate;
}
@Override
public void run() {
try {
// 5.ScheduledRunnable.run()方法在Handler.dispatchMessage()中被执行,
// 然后调用外部传入的真正的Runnable。
delegate.run();
} catch (Throwable t) {
RxJavaPlugins.onError(t);
}
}
}
}
小结: 线程切换到主线程的流程:
HandlerScheduler.createWorker()
创建了一个HandlerWorker
对象。- 构建一个Runnable对象,里面包含了将线程进行切换的Handler和外部传入的Runnable。
- 将该 Runnable 对象封装到 Msg.callback 字段中,这个Runnable会在Handler.dispatchMessage()中被执行。
- 将消息发送到主线程中接收,于是就实现了线程的切换。
- 主线程的Looper接收到消息后,会按照以下流程执行:
Handler.dispatchMessage() --> ScheduledRunnable.run() --> 外部 Runnable.run()
。