一、分析observeOn()方法的线程切换
1、示例代码
public static void changeThread() {
Log.d("TAG", "当前程序所在的线程 " + Thread.currentThread().getName());
Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter e) throws Exception {
e.onNext(1);
e.onNext(2);
e.onNext(3);
e.onComplete();
Log.d("TAG", "被观察者所在的线程 " + Thread.currentThread().getName());
}
});
observable
.observeOn(Schedulers.io())
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
Log.d("TAG", "subscribe>>onSubscribe");
}
@Override
public void onNext(Integer integer) {
Log.d("TAG", "subscribe>>onNext:" + integer);
Log.d("TAG", "观察者所在的线程 " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
Log.d("TAG", "subscribe>>onError:" + e.getMessage());
}
@Override
public void onComplete() {
Log.d("TAG", "subscribe>>onComplete");
}
});
}
日志如下:
D/TAG: 当前程序所在的线程 main
D/TAG: subscribe>>onSubscribe
D/TAG: 被观察者所在的线程 main
D/TAG: subscribe>>onNext:1
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onNext:2
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onNext:3
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onComplete
2、源码分析
observeOn()方法的源码如下:
public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
ObjectHelper.verifyPositive(bufferSize, "bufferSize");
return RxJavaPlugins.onAssembly(new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize));
}
最终返回一个ObservableObserveOn对象,ObservableObserveOn是Observable的子类。当调用subscribe()方法时会回调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));
}
}
如果传入的scheduler是Scheduler.trampoline(),Scheduler.trampoline()是指传入当前线程,也就是不做任何线程切换操作。一般情况不会这样操作。所以上面代码会执行else部分。
source(上游Observable)和Observable.subscribe()操作都没有任何变化,唯一改变的地方就是将Observer进行了封装,所以我们可以因此得出结论, Observable.observeOn(Scheduler) 并不会对上游线程执行环境有任何影响。
Observer的onNext()源码如下:
@Override
public void onNext(T t) {
......
if (sourceMode != QueueDisposable.ASYNC) {
queue.offer(t);
}
schedule();
}
void schedule() {
if (getAndIncrement() == 0) {
worker.schedule(this);
}
}
点进schedule发现是Schedule.Worker一个抽象方法,我们要到实现类中查看具体实现,这里使用Schedule.Worker的实现类是IoScheduler.EventLoopWorker。IoScheduler.EventLoopWorker中schedule方法源码如下:
@Override
public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
if (tasks.isDisposed()) {
return EmptyDisposable.INSTANCE;
}
return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}
scheduleActual()的源码如下:
public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);
......
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) {
......
RxJavaPlugins.onError(ex);
}
return sr;
}
executor是一个ScheduledExecutorService对象,而ScheduledExecutorService的父接口是我们所熟悉的ExecutorService接口,所以很清晰ScheduledExecutorService具有创建和调度线程的能力,而其具体的实现在此就不讨论了。
总结:
onNext(T)方法会触发Scheduler对象的schedule(Runnable, long, TimeUnit),该方法是一个抽象方法,由子类实现,所以才有了多种多样的 Schedulers.io()、Schedulers.computation()、Schedulers.trampoline()等调度器,具体调度器的内部会使用相关的线程来submit()或者schedule()任务。
解决完调度器的问题,那么接下来就是看看Runnable.run()里面的逻辑是什么样的,回到ObserveOnObserver中,源码如下:
@Override
public void run() {
if (outputFused) {
drainFused();
} else {
drainNormal();
}
}
outputFused默认是false,所有一般都执行drainNormal(),源码如下:
void drainNormal() {
final SimpleQueue<T> 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);
}
......
}
}
可以看到实际上最后一行执行Observer#onNext(T)方法,也就是意味着ObserveOnObserver 中触发下一层Observer的onNext(T)操作在指定线程执行,也就达到了切换线程的目的。
3、来个复杂的例子
经过 RxJava2.x 源码解析(一)基本订阅流程一文我们知道,Observer 的传递是由下往上的,从源头开始,我们自定义的 Observer向上传递的时候到达第六个Observable 的时候被线程封装了一层,我们不妨使用伪代码演示一下。
new Thread("Scheduler io") {
@Override
public void run() {
// flatMap() 操作
flatMap();
System.out.println("flatMap 操作符执行线程:" + Thread.currentThread().getName());
System.out.println("第二个 observeOn() 执行线程:" + Thread.currentThread().getName());
// 第二个 observeOn() 操作
new Thread("Scheduler computation") {
@Override
public void run() {
// map() 操作
map();
System.out.println("map 操作符执行线程:" + Thread.currentThread().getName());
System.out.println("第三个 observeOn() 执行线程:" + Thread.currentThread().getName());
// 第三个 observeOn() 操作
new Thread("Android mainThread") {
@Override
public void run() {
// Observer#onNext(T)/onComplete()/onError() 执行线程
System.out.println("Observer#onNext(T)/onComplete()/onError() 执行线程:" +
Thread.currentThread().getName());
}
} .start();
}
} .start();
}
} .start();
简而言之Observable.observeOn(Scheduler)的实现原理在于将目标Observer的 onNext(T)、onError(Throwable)、onComplete()置于指定线程中运行。
二、分析subscribeOn()方法的线程切换
1、示例代码
public static void changeThread() {
Log.d("TAG", "当前程序所在的线程 " + Thread.currentThread().getName());
Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter e) throws Exception {
e.onNext(1);
e.onNext(2);
e.onNext(3);
e.onComplete();
Log.d("TAG", "被观察者所在的线程 " + Thread.currentThread().getName());
}
});
observable
.subscribeOn(Schedulers.io())
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
Log.d("TAG", "subscribe>>onSubscribe");
}
@Override
public void onNext(Integer integer) {
Log.d("TAG", "subscribe>>onNext:" + integer);
Log.d("TAG", "观察者所在的线程 " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
Log.d("TAG", "subscribe>>onError:" + e.getMessage());
}
@Override
public void onComplete() {
Log.d("TAG", "subscribe>>onComplete");
}
});
}
日志如下:
D/TAG: 当前程序所在的线程 main
D/TAG: subscribe>>onSubscribe
D/TAG: subscribe>>onNext:1
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onNext:2
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onNext:3
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onComplete
D/TAG: 被观察者所在的线程 RxCachedThreadScheduler-1
2、源码分析
subscribeOn的源码如下:
public final Observable<T> subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
}
在订阅时最终会调用到ObservableSubscribeOn的subscribeActual方法,我们看看其源码:
@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 Runnable() {
@Override
public void run() {
source.subscribe(parent);
}
}));
}
第一行老套路,对下游Observer进行了一层封装,第二行因为它不涉及线程切换所以此处也不做扩展,第三行Scheduler.scheduleDirect(Runnable)就是切换线程的了。scheduleDirect源码如下:
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
final Worker w = createWorker();
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
w.schedule(new Runnable() {
@Override
public void run() {
try {
decoratedRun.run();
} finally {
w.dispose();
}
}
}, delay, unit);
return w;
}
这里实现线程切换是w中,我们需要看看createWorker的源码:
public abstract Worker createWorker();
在Scheduler中是一个抽象方法,因为我们要去其实现类IoScheduler中看看具体实现:
@Override
public Worker createWorker() {
return new EventLoopWorker(pool.get());
}
static final class EventLoopWorker extends Scheduler.Worker {
private final CompositeDisposable tasks;
private final CachedWorkerPool pool;
private final ThreadWorker threadWorker;
final AtomicBoolean once = new AtomicBoolean();
EventLoopWorker(CachedWorkerPool pool) {
this.pool = pool;
this.tasks = new CompositeDisposable();
this.threadWorker = pool.get();
}
......
@NonNull
@Override
public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
if (tasks.isDisposed()) {
return EmptyDisposable.INSTANCE;
}
return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}
}
最终是通过threadWorker.scheduleActual(action, delayTime, unit, tasks);实现线程切换,scheduleActual源码如下:
public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);
......
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) {
......
RxJavaPlugins.onError(ex);
}
return sr;
}
executor是一个ScheduledExecutorService对象,而ScheduledExecutorService的父接口是我们所熟悉的ExecutorService接口,所以很清晰ScheduledExecutorService具有创建和调度线程的能力,而其具体的实现在此就不讨论了。
3、来个复杂的例子
日志如下:
onNext(T)/onError(Throwable)/onComplete() 的执行线程是: io
前面我们分析到Observable.subscribeOn(Scheduler) 实际上是将 Observable.subscribe(Observer)的操作放在指定线程,而通过RxJava2.x源码解析(一)订阅流程一文我们知道了subscribe的过程是由下往上的。所以首先是第三个 Observable调用Observable.subscribe(Observer)启动订阅,在其内部会激活第二个 Observable的Observable.subscribe(Observer)方法,但是此时该方法外部被套入了一个 Schedulers.computation()线程,于是这个订阅的过程就被运行在了该线程中。我们不妨用伪代码演示如下:
new Thread("computation") {
@Override
public void run() {
// 第二个 Observable.subscribe(Observer) 的实质
// 就是切换线程,效果类似如下
new Thread("io") {
@Override
public void run() {
// 第一个 Observable.subscribe(Observer) 的实质
// 就是发射事件
System.out.println("onNext(T)/onError(Throwable)/onComplete() 的执行线程是: " + Thread
.currentThread().getName());
}
} .start();
}
} .start();
因此使用多个subscribeOn时只有第一个有效。