接上篇继续。
一、线程切换observeOn
Observable.just("1")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer)
我们知道,当调用observeOn之后,后面执行的代码都将切换到他指定的线程,除非再次调用observeOn切换其他线程。上源码:
@CheckReturnValue
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable observeOn(Scheduler scheduler) {
return observeOn(scheduler, false, bufferSize());
}
@CheckReturnValue
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
ObjectHelper.verifyPositive(bufferSize, "bufferSize");
return RxJavaPlugins.onAssembly(new ObservableObserveOn(this, scheduler, delayError, bufferSize));
}
经过第一篇的分析,套路已经很熟悉了。最终就是new了一个ObservableObserveOn并返回,而这个ObservableObserveOn估计也是Observable的子类。进去看一下。
public final class ObservableObserveOn extends AbstractObservableWithUpstream {
final Scheduler scheduler;
final boolean delayError;
final int bufferSize;
public ObservableObserveOn(ObservableSource source, Scheduler scheduler, boolean delayError, int bufferSize) {
super(source);
this.scheduler = scheduler;
this.delayError = delayError;
this.bufferSize = bufferSize;
}
}
abstract class AbstractObservableWithUpstream extends Observable implements HasUpstreamObservableSource {
/** The source consumable Observable. */
protected final ObservableSource source;
/**
* Constructs the ObservableSource with the given consumable.
* @param source the consumable Observable
*/
AbstractObservableWithUpstream(ObservableSource source) {
this.source = source;
}
@Override
public final ObservableSource source() {
return source;
}
}
果然不出所料。这样就简单了,我们只要分析他的实现方法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(observer, w, delayError, bufferSize));
}
}
createWorker方法,具体实现在我们传入的Scheduler:
final class HandlerScheduler extends Scheduler {
private final Handler handler;
HandlerScheduler(Handler handler) {
this.handler = handler;
}
@Override
public Worker createWorker() {
return new HandlerWorker(handler);
}
}
static final Scheduler DEFAULT = new HandlerScheduler(new Handler(Looper.getMainLooper()));
private static final class HandlerWorker extends Worker {
private final Handler handler;
private volatile boolean disposed;
HandlerWorker(Handler handler) {
this.handler = handler;
}
@Override
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.
handler.sendMessageDelayed(message, Math.max(0L, 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;
}
@Override
public void dispose() {
disposed = true;
handler.removeCallbacksAndMessages(this /* token */);
}
@Override
public boolean isDisposed() {
return disposed;
}
}
代码有点多,一点点分析。创建的worker叫HandlerWorker。他有个schedule方法,是执行runnable的。再看这句是重点:
Message message = Message.obtain(handler, scheduled);
把scheduled作为一个runnable发到消息队列中,通过looper循环以后交给handler去处理。而这里的handler对应的looper是主线程的,所以runnable的代码就运行在主线程。最终完成了线程的切换(切换到主线程)。不是很熟悉handler代码的同学,我在这里贴下代码吧:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
msg.callback就是我们之前创建messge对象的时候传入的scheduled,也就是一个runnable,然后去执行他的run方法。
再回过来,那么scheduled到底是个什么呢?
private static final class ScheduledRunnable implements Runnable, Disposable {
private final Handler handler;
private final Runnable delegate;
private volatile boolean disposed;
ScheduledRunnable(Handler handler, Runnable delegate) {
this.handler = handler;
this.delegate = delegate;
}
@Override
public void run() {
try {
delegate.run();
} catch (Throwable t) {
IllegalStateException ie =
new IllegalStateException("Fatal Exception thrown on Scheduler.", t);
RxJavaPlugins.onError(ie);
Thread thread = Thread.currentThread();
thread.getUncaughtExceptionHandler().uncaughtException(thread, ie);
}
}
}
其实只是runnable的包装类,如果你的代码出了异常,这里做了处理。
好了,我们回顾一下之前的思路。我们为了看createWorker方法的具体实现进来,发现创建的是HandlerWorker,而HandlerWorker里面有个schedule方法,他的作用是把传入的runnable参数包装成ScheduledRunnable以后,通过handler让他运行在主线程,完成线程的切换。ok,问题已经变得简单了,我们只要找到哪里调用了schedule方法,就完全明白了。
回到ObservableObserveOn#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(observer, w, delayError, bufferSize));
}
}
第一步创建worker以后,然后调用了source的subscribe方法。source我们上篇分析过,他是创建当前Observable的Observable,那么ObserveOnObserver又是什么呢?
static final class ObserveOnObserver extends BasicIntQueueDisposable
implements Observer, Runnable {
ObserveOnObserver(Observer super T> actual, Scheduler.Worker worker, boolean delayError, int bufferSize) {
this.actual = actual;
this.worker = worker;
this.delayError = delayError;
this.bufferSize = bufferSize;
}
@Override
public void onNext(T t) {
if (done) {
return;
}
if (sourceMode != QueueDisposable.ASYNC) {
queue.offer(t);
}
schedule();
}
void schedule() {
if (getAndIncrement() == 0) {
worker.schedule(this);
}
}
}
看到没,他就是个Observer的封装类,持有我们传入的observer的引用。装饰者模式真是无处不在啊。以onNext为例,又会去调用schedule()。bingo,这里面正是调用了worker的sechedule方法。终于对上了。执行的runnable是this,那么看下他的run方法:
@Override
public void run() {
if (outputFused) {
drainFused();
} else {
drainNormal();
}
}
void drainNormal() {
int missed = 1;
final SimpleQueue q = queue;
final Observer super T> a = actual;
for (;;) {
if (checkTerminated(done, q.isEmpty(), a)) {
return;
}
for (;;) {
boolean d = done;
T v;
try {
v = q.poll();
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
s.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;
}
}
}
最终调用了我们自己的observer#onNext
我再重新梳理一遍:
@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(observer, w, delayError, bufferSize));
}
}
先是创建一个worker,然后通过source#subscribe间接调用了worker#schedule,让runnable执行在主线程。而这个runnable又持有了observer,会调用observer的onNext等方法。
从这里我们也发现了,我们自己的observer执行的onNext,实际上是被锁死在了observeOn指定的线程了(前提是要调用observeOn方法)。
二、关于线程切换你必须要知道的一些事
在说知道的事之前,我们先来分析下代码,这有助于同学们理解我想说的事。
Observable.just("1") //1号
.observeOn(Schedulers.io()) //2号
.subscribeOn(Schedulers.io()) //3号
.subscribeOn(AndroidSchedulers.mainThread()) //4号
.observeOn(AndroidSchedulers.mainThread()) //5号
.map {
return@map it.plus("2")
} //6号
.observeOn(Schedulers.io()) //7号
.subscribe(observer)
这里我为了分析的需要,故意来回切换线程。看注释,通过之前的分析,我们知道实际上每一行都生成了一个Observable的子类,为了方便区分,我后面就用1号,2号来称呼。
我们来捋一捋,注意,接下来的内容一定要弄懂!
1号是事件源,1号创建了2号然后把自己传给2号,2号创建了3号把自己传给3号,3号创建了4号把自己传给4号,4号创建了5号把自己传给5号,5号创建了6号把自己传给6号,6号创建了7号把自己传给7号。而每个Obsersable里面都对应一个observer。 最终7号Obsersable调用subscribe,把我们的observer传进来(后面我都称呼为原始observer)。这时候,会去调用7号的subscribeActual,具体实现是用7号observer装饰原始observer,再去调用之前传进去的6号的subscribeActual方法,6号的subscribeActual方法具体实现又是用6号的observer装饰7号装饰过的observer,再去调用之前传进去的5号的subscribeActual方法。以此类推,最终调用1号的subscribeActual,并把经过层层装饰的observer传进去。1号是事件源,我们在第一篇已经分析过,他的subscribeActual最终会去调用传进来的observer的onNext方法,实际会去调用2号装饰过的observer#onNext,2号装饰过的observer因为装饰了3号的observer,又会去调用3号observer装饰过的observer,以此类推,最终会调用原始observer(也就是我们自己的observer)。
像不像套娃娃,observable创建的时候是从1号开始套到7号,执行的时候是从7号执行到1号;而observer恰好相反,创建的时候从原始observer开始套到1号,执行的时候从1号到原始observer。
好了,现在开始说关于线程切换你需要知道的事!
1、subscribeOn只有第一次设置是有效的
通过上面的分析,我们知道observable#subscribeActual执行顺序是从7到1,那么无论之后调用了几次subscribeOn,执行到后面肯定要经过第一次的subscribeOn,在图中就是2号。2号是运行在线程池,所以之后的执行1号也就在线程池。这就好比管你们之前怎么折腾,老子在终点等你们。
2、observeOn设置多次都是有效的,并且每设置一次后面的代码就会切换到相应的线程。
通过上面的分析,我们知道observer#onNext执行顺序是从1到原始observer。那么2号对应的observer#onNext就执行在线程池,接下来5号又把线程切换到了主线程,那么6号对应的observer#onNext就执行在主线程,7号又把对应的observer#onNext带到了线程池,所以最终原observer#onNext执行在线程池。
说些题外话
Scheduler实际就相当于java的executor,Ioscheduler、MainScheduler就相当于线程池,Schedulers就相当于Executors。为什么是这样这里就不说了,请同学们自己看源码理解吧。
其实Rxjava2还有几十个操作符,如果一个个分析需要太多篇文章也没有必要。套路是差不多的,都是创建observable的子类,有兴趣的同学自己去触类旁通吧。如果有写得不正确的欢迎指正,也欢迎留言探讨。