原理图
- 图中表示的三条流和事件传递
- 剖析线程切换的原理
流式构建和事件传递
之前我们又提过Rxjava
中有Observable
和Observer
两个核心概念,他们在发生订阅时,和普通的观察者模式写法不一样,通常的写法中是观察者(Observer
)去订阅(Subscribe
)被观察者(Observable
),但是Rxjava为了其基于事件的流式编程,所以只能反着来,Observable
去订阅Observer
,所以在Rxjava中,Subscribe
可以理解是“注入”观察者(Observer
)。
现在我们来简单的解释下上面的图片:
- 图中的方框代表是
Observable
,因为它代表着节点,用Ni
表示 - 原形框表示观察者
Observer
,用Oi
表示,后面括号()
中是表示当前Oi
持有其下游Observer
的应用,左侧表示上游,右侧代表下游。
图片有三条有方向的彩色粗线,代表着三个不同的流,这三个流是我们为了分析问题而抽象出来的,代表从构建到订阅整个事件的流向,按照时间顺序从上到下依次流过,它们的含义分别是:
- 从左往右的构建流:用来构建整个事件序列,这个流表现了整个链路的构建过程。
- 从右往左的订阅流:当构建完成后,调用订阅(
subscribe(...)
方法)这个行为发生的时候,每个节点从右向左依次执行订阅行为。 - 从左往右的观察者回调流(
事件回调流
):订阅完成后,当有事件被发送以后,会通过这个流一次通知给各个观察者。
依次分析这三条流:
构建流
在使用Rxjava时,它的流式构建流程是很大的特色,避免了传统回调的繁琐。实现手法:在Rxjava的每一步构建过程的api都是相同,这是因为每一步的函数返回结果都是一个Observable
,这个Observable
提供了Rxjava的所有功能。Observable
这个到底扮演了什么角色?我们从官方看到了答案:“usingObservable sequences”,所以说Observable
就是构建流的组件,我们可以看成一个个的节点,这些节点穿起来组成整个链路。这个有点像建造者模式,只不过建造者返回的始终是一个对象实例,而Rxjava每次创建新的Observable
并返回。Observable
这个类实现了一个接口ObservableSource
,这个借口有一个方法Subscribe(Observer)
,也就是说,所有的Observable
节点都具有订阅observer()
这个功能。
总结
在我们编写Rxjava代码时,每一步操作都会生成一个新的Observable节点(没错,包括ObserveOn和SubscribeOn线程变换操作),并将新生成的Observable返回,直到最后一步执行subscribe方法。
无论是构建的第一步create()
方法,还是observeOn()
,subscribeOn
变换线程方法,还是各种操作符比如map
,flatMap
等,都会生成对应的Observable,每个Observble中要实现一个最重要的方法就是subscribe(),我们看其实现:
public final void subscribe(Observer<? super T> observer) {
ObjectHelper.requireNonNull(observer, "observer is null");
try {
observer = RxJavaPlugins.onSubscribe(this, observer);
subscribeActual(observer);
} catch (NullPointerException e) { // NOPMD
throw e;
} catch (Throwable e) {
Exceptions.throwIfFatal(e);
RxJavaPlugins.onError(e);
npe.initCause(e);
throw npe;
}
}
RxJavaPlugins里面的代码可以过滤掉,里面是hook用的,不会影响流程,主要关注下 subscribeActual(observer)方法。
也就是说,每个节点在执行subscribe时,其实就是在调用该节点的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> observer) {
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(observer);
observer.onSubscribe(parent);
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
...
}
abstract class AbstractObservableWithUpstream<T, U> extends Observable<U> implements HasUpstreamObservableSource<T> {
/** The source consumable Observable. */
protected final ObservableSource<T> source;
}
其中其父类继承的是Observable,所以ObservableSubscribeOn是一个Observable。
总结下:
在整个构建过程中有点像build
模式,不同之处是它每次构建都生成了新的节点,而build
模式返回的是对象的本身,这个有点像OKhttp
中拦截器,okHttp中会构建多个Chain节点,然后用相应的Intercepter去处理Chain。在编写Rxjava代码的过程其实就是构建一个个Observable节点的过程。
订阅流
构建过程只是通过构造函数将一些配置传给各个节点(N1~N5),实际还没有执行任何代码,只有最有一步(subscribe()
方法)才是真正的执行订阅行为。当最后一个节点调用subscribe()
方法时,是构建流向订阅流变化的转折点,以上图为例子:
最后一个节点是N5,N5节点后调用这个节点的subscribe()
,这个方法最终会调用subscribeActual()
方法中去,看看源码:
public final class ObservableFlatMap<T, U> extends AbstractObservableWithUpstream<T, U> {
final Function<? super T, ? extends ObservableSource<? extends U>> mapper;
final boolean delayErrors;
final int maxConcurrency;
final int bufferSize;
public ObservableFlatMap(ObservableSource<T> source,
Function<? super T, ? extends ObservableSource<? extends U>> mapper,
boolean delayErrors, int maxConcurrency, int bufferSize) {
super(source);
this.mapper = mapper;
this.delayErrors = delayErrors;
this.maxConcurrency = maxConcurrency;
this.bufferSize = bufferSize;
}
@Override
public void subscribeActual(Observer<? super U> t) {
if (ObservableScalarXMap.tryScalarXMapSubscribe(source, t, mapper)) {
return;
}
source.subscribe(new MergeObserver<T, U>(t, mapper, delayErrors, maxConcurrency, bufferSize));
}
}
刚刚分析了,N5节点是Observable节点,其中subscribe方法最后调用了subscribeActual方法,最终调用了:
source.subscribe(new MergeObserver<T, U>(t, mapper, delayErrors, maxConcurrency, bufferSize));
MergeObserver(Observer<? super U> actual, Function<? super T, ? extends ObservableSource<? extends U>> mapper,
boolean delayErrors, int maxConcurrency, int bufferSize) {
this.downstream = actual;
this.mapper = mapper;
this.delayErrors = delayErrors;
this.maxConcurrency = maxConcurrency;
this.bufferSize = bufferSize;
if (maxConcurrency != Integer.MAX_VALUE) {
sources = new ArrayDeque<ObservableSource<? extends U>>(maxConcurrency);
}
this.observers = new AtomicReference<InnerObserver<?, ?>[]>(EMPTY);
}
这段代码解析:
- 生成一个新的Observer,第一个参数
t
保存到了downstream
这个变量中,这个变量哪传入的呢?对于N5节点来说,这个t
就是我们代码最后一步编写的Observer,比如我们常用的网络请求返回后的回调,这个新生成的Observer包含它的“下游”观察者的应用,在图片中对于的是圆形框O1(observer)。 - 执行订阅行为,这里的source是该节点构造函数传入的source,通过源码得知其实就是N5节点的上一个节点N4,因此,这里的订阅行为本质上是让当前节点的上一个节点订阅当前节点新生成的Observer。
到这里总结分析下:
每个节点的执行流程都是类似的(subscribeOn节点有些特殊,只不过里面有加入线程调度的操作),也就是说N5节点会调用N4的subscribe方法,在N4的subscribe方法中又调用了N3的subscribe方法…一直到N0会调用到source的subscribe方法。
从最后一个N5节点的订阅行为开始,一次执行前面各个节点真正的订阅方法。在每个节点的订阅方法中中,都会生成一个新的Observer,而这个Observer会包含“下游”的Observer,这样当每个节点都执行订阅(subscribeActual方法)后,也就生成了一串Observer,他们通过downstream,upstream引用连接。
用及其简单的一句话就是:
下游节点调用上游节点的subscribeActual方法,直至调用到source的的subscribe,完成整个订阅流。
观察者回调流
当订阅流执行到最后,也就是第一个节点N0时,我们看发生了什么,首先看看N0节点怎么建立的?
@CheckReturnValue
@NonNull
@SchedulerSupport(SchedulerSupport.NONE)
public static <T> Observable<T> create(ObservableOnSubscribe<T> source) {
ObjectHelper.requireNonNull(source, "source is null");
return RxJavaPlugins.onAssembly(new ObservableCreate<T>(source));
}
生成了ObservableCreate
实例:
public final class ObservableCreate<T> extends Observable<T> {
final ObservableOnSubscribe<T> source;
public ObservableCreate(ObservableOnSubscribe<T> source) {
this.source = source;
}
@Override
protected void subscribeActual(Observer<? super T> observer) {
CreateEmitter<T> parent = new CreateEmitter<T>(observer);
observer.onSubscribe(parent);
try {
source.subscribe(parent);
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
parent.onError(ex);
}
}
}
所有订阅流的最终会调用到这个类的subscribeActual方法,它其实还是和其他节点,最主要的还是执行:
source.subscribe(parent);
这个节点的source是事件的源头,看看subscribe方法:
Observable.create(object : ObservableOnSubscribe<String> {
override fun subscribe(emitter: ObservableEmitter<String>) {
// 执行耗时任务
val str = "123"
emitter.onNext(str)
}
})
这个是开头的例子,这个source是一个ObservableOnSubscribe
,这个的subscribe
方法就是订阅流和观察者流的转折点,也就是流在这转向了。这里这个事件源没有像节点那样,调用上一个节点的订阅方法,而是调用了其参数的emitter
的onNext
方法,这个emitter
对应N0节点什么呢?看看源码:
static final class CreateEmitter<T>
extends AtomicReference<Disposable>
implements ObservableEmitter<T>, Disposable {
private static final long serialVersionUID = -3434801548987643227L;
final Observer<? super T> observer;
CreateEmitter(Observer<? super T> observer) {
this.observer = observer;
}
@Override
public void onNext(T t) {
if (t == null) {
onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
return;
}
if (!isDisposed()) {
observer.onNext(t);
}
}
...
}
在执行了onNext方法,是执行了observer.onNext
方法。
这个N0节点subscribeActual方法中的observer,这个observer是谁呢?仔细回想一下,前面订阅流的时候不就是一次订阅上一个节点生成的Observer吗,所以这个observer就是前一个节点N1生成的Observer。
static final class MapObserver<T, U> extends BasicFuseableObserver<T, U> {
final Function<? super T, ? extends U> mapper;
MapObserver(Observer<? super U> actual, Function<? super T, ? extends U> mapper) {
super(actual);
this.mapper = mapper;
}
@Override
public void onNext(T t) {
if (done) {
return;
}
if (sourceMode != NONE) {
downstream.onNext(null);
return;
}
U v;
try {
v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.");
} catch (Throwable ex) {
fail(ex);
return;
}
downstream.onNext(v);
}
@Override
public int requestFusion(int mode) {
return transitiveBoundaryFusion(mode);
}
@Nullable
@Override
public U poll() throws Exception {
T t = qd.poll();
return t != null ? ObjectHelper.<U>requireNonNull(mapper.apply(t), "The mapper function returned a null value.") : null;
}
}
名为MapObserver,看它的onNext方法,忽略前面两个判断语句,核心就两句,一个是mapper.apply(t),另一个就是downstream.onNext(v)。也就是说,这个mapObserver干了两件事,一个是把上个节点返回的数据进行一次map变换,另一个就是将map后的结果传递给下游,下游是什么呢?就是N2节点的Observer,对应图中O4,依次类推,我们知道了,事件发生以后,通过各个节点的Observer事件源被层层处理并传递给下游,一直到最后一个观察者执行完毕,整个事件处理完成。
线程调度
图中N2节点左侧的所有节点和右侧的节点颜色不同,这是要说明线程不同,如何进行线程的调度呢?这就要使用到subscribeOn
和observerOn
节点进行线程调度。
- subscribeOn
在订阅流发生的的时候,大多数节点都是直接调用上一个节点的subscribe方法,实现虽有差别,但大同小异。唯一有个最大的不同就是subscribeOn这个节点
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> observer) {
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(observer);
observer.onSubscribe(parent);
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
}
普通的节点执行时,大多只是简单的执行source.subscribe(observer),但是这个不一样。先看第二行,它调用了观察者的onSubscribe方法,熟悉Rxjava的人知道,我们在自定义Observer的时候,里面有这个回调,其发生时机就在此刻。我们接着看最后一行,忽略parent.setDisposable这个逻辑,我们直接看参数里面的东西。
scheduler.scheduleDirect(new SubscribeTask(parent));
public Disposable scheduleDirect(@NonNull Runnable run) {
return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
}
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;
}
// 创建了一个worker,一个runnable,然后将二者封装到一个DisposeTask中,最后用worker执行这个task
public abstract Worker createWorker();
// createworker是一个抽象方法,所以需要去找Scheduler的子类,我们回想一下rxjava的使用,如果在子线程中执行,我们一般设置调度器为Schedulers.io(),我们看这个子类的实现:在IOSchedluer类中:
@Override
public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
if (tasks.isDisposed()) {
// don't schedule, we are unsubscribed
return EmptyDisposable.INSTANCE;
}
return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}
// 接下来
// 这里的executor就是一个ExecutorService,熟悉线程池的读者应该知道,这里的submit方法,就是将callable丢到线程池中去执行任务了。
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;
}
回过来看:
scheduler.scheduleDirect(new SubscribeTask(parent))
对于io线程的调度器来说,上面的代码就是将new SubscribeTask(parent)丢到线程池中执行,我们看参数里面的SubscribeTask:
final class SubscribeTask implements Runnable {
private final SubscribeOnObserver<T> parent;
SubscribeTask(SubscribeOnObserver<T> parent) {
this.parent = parent;
}
@Override
public void run() {
source.subscribe(parent);
}
}
看run方法:
source.subscribe(parent)
,这里的parent跟普通节点一样,仍然是本节点生成的新的Observer,对于本节点来说,是一个SubscribeOnObserver。因此,对于subscribeOn
这个节点,它与普通节点不同之处:
subscribeOn节点在订阅的时候,将它的上游节点的订阅行为,以runnable的形式扔给了一个线程池(对于IO调度器来说),也就是说,当订阅流流到subscribeOn节点时,线程发生了切换,之后流向的节点都在切换后的线程中执行。
分析到这里,我们就知道了subscribeOn的线程切换原理了,原来是在订阅流中塞了一个线程变化操作。我们再看图中的颜色问题,为什么这个节点上游的节点都是红色的呢?因为当订阅流流过这个节点后,后面的节点只是单纯的传递给上游节点而已,无论是普通的操作符,还是observeOn节点,都是简单的传递给上游,没有做线程切换
问题思考:
我们再思考一个问题,如果上游还有别的subscribeOn,会发生什么?
我们假设N1节点的map修改程subscribeOn(AndroidScheduler.Main),也就是说,切换到主线程。我们还是从N2节点开始分析,刚才说到最后会执行到SubscribeTask里的Run方法,注意此时source.subscribe(parent)发生在子线程中,接下来,回调用N1节点的subscribe,N1节点回调用scheduler.scheduleDirect(new SubscribeTask(parent)),方法,此时,因为线程调度器是主线程的,我们看它的代码:
private static final class MainHolder {
static final Scheduler DEFAULT
= new HandlerScheduler(new Handler(Looper.getMainLooper()), false);
}
看看 HandlerScheduler方法:
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;
}
看到最终是通过Handler来进行切换到主线程。
熟悉Android Handler机制的读者应该很清楚,这里会把N1节点上游的操作,通过Handler机制,扔给主线程操作,虽然这一步是在N2节点的子线程中执行的,但是它之前的事件仍然会在主线程中执行。因此我们有以下结论:
subscribeOn节点影响它前面的节点的线程,如果前面还有多个subscribeOn节点,最终只有第一个,也就是最上游的那个节点生效。
- observerOn
前面的subscribeOn线程切换是在订阅流中发生的,接下来的ObserveOn比较简单,它发生在第三条流(观察者回调流)中,我们看ObserveOn节点的源码:
ObserveOnObserver(Observer<? super T> actual, Scheduler.Worker worker, boolean delayError, int bufferSize) {
this.downstream = actual;
this.worker = worker;
this.delayError = delayError;
this.bufferSize = bufferSize;
}
@Override
public void onSubscribe(Disposable d) {
if (DisposableHelper.validate(this.upstream, d)) {
this.upstream = d;
if (d instanceof QueueDisposable) {
@SuppressWarnings("unchecked")
QueueDisposable<T> qd = (QueueDisposable<T>) d;
int m = qd.requestFusion(QueueDisposable.ANY | QueueDisposable.BOUNDARY);
if (m == QueueDisposable.SYNC) {
sourceMode = m;
queue = qd;
done = true;
downstream.onSubscribe(this);
schedule();
return;
}
if (m == QueueDisposable.ASYNC) {
sourceMode = m;
queue = qd;
downstream.onSubscribe(this);
return;
}
}
queue = new SpscLinkedArrayQueue<T>(bufferSize);
downstream.onSubscribe(this);
}
}
@Override
public void onNext(T t) {
if (done) {
return;
}
if (sourceMode != QueueDisposable.ASYNC) {
queue.offer(t);
}
schedule();
}
}
在前面的观察者流分析时,我们知道,观察者流是通过onNext()方法传递的,我们看最后一行,schedule(),线程切换,所以这个ObserveOn节点其实没干啥事,就是切换线程了,而且是在onNext回调中切换的。我们进到这个方法:
void schedule() {
if (getAndIncrement() == 0) {
worker.schedule(this);
}
}
worker是这个节点订阅时指定的 scheduler.createWorker(),以切换到主线程为例,见类HandlerScheduler源码:
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机制,将runnable扔给主线程执行,runnable是谁呢,是this,this就是这个ObserveOnObserver,我们看它的run方法:
@Override
public void run() {
if (outputFused) {
drainFused();
} else {
drainNormal();
}
}
继续看drainNormal
void drainNormal() {
int missed = 1;
final SimpleQueue<T> q = queue;
final Observer<? super T> a = downstream;
for (;;) {
if (checkTerminated(done, q.isEmpty(), a)) {
return;
}
for (;;) {
boolean d = done;
T v;
try {
v = q.poll();
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
disposed = true;
upstream.dispose();
q.clear();
a.onError(ex);
worker.dispose();
return;
}
boolean empty = v == null;
if (checkTerminated(d, empty, a)) {
return;
}
if (empty) {
break;
}
a.onNext(v);
}
missed = addAndGet(-missed);
if (missed == 0) {
break;
}
}
}
总结:
最终还是把上游的处理结果扔给下游,也就是说observerOn会将它下游的onNext操作扔给它切换的线程中,因此ObserverOn影响是它的下游,所以我们图中的observerOn后面的颜色都是蓝色的。
思考:
如果有多个observeOn会发生什么?
解答:
很简单,思路同subscribeOn,每个ObserveOn只会影响它下游一直到下一个obseveOn节点的线程,也就是分段的。
总结
Rxjava有点像观察者模式和责任链模式的结合变种,普通的观察者模式一半是被观察者通知多个观察者,而Rxjava则是被观察者通知第一个Observer,接下来Observer依次通知其他节点的Observer,将观察者模式进行了链式的变换,每个节点又执行它不同的职责,非常巧妙,事件在Observable链条上进行传递,事件结果通过Observer链条进行回调,这就是Rxjava的精髓!
作者发言:下一篇是手写简易的Rxjava实现,便于更加理解Rxjava精髓。