Rxjava 高频面试题详解线程切换的原理
Rxjava 面试中最高频的就是线程切换这块,我们可以进行将问题进行拆解
- subscribeOn 是怎么样把上游的工作的节点切换到指定线程上面的去的?
- observerOn 是怎么样把下游的工作的节点切换到指定线程上面的去的?
1.subscribeOn执行之后会创建一个ObservableSubscribeOn内部包含了我们上的souce和指定的Scheduler
,Scheduler我们就可以理解为指定线程,一旦Observer进行订阅了之后就会调用我们的subscribeActual
关键代码来了
// 将上一层的sourcer任务切换到 SubscribeTask中执行
scheduler.scheduleDirect(new SubscribeTask(parent))
// SubscribeTask 就是一个runnable
final class SubscribeTask implements Runnable {
private final SubscribeOnObserver<T> parent;
SubscribeTask(SubscribeOnObserver<T> parent) {
this.parent = parent;
}
@Override
public void run() {
// 执行任务的地方
source.subscribe(parent);
}
}
而执行这个runnable 是在哪里呢,我们就要来看我们的Schedulers.io()这个线程策略了
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
// 这个Worker 就是我们 IoScheduler. EventLoopWorker
final Worker w = createWorker();
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
DisposeTask task = new DisposeTask(decoratedRun, w);
//
w.schedule(task, delay, unit);
return task;
}
最后就会执行到我们的 NewThreadWorker scheduleActual
public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
...........
//
try {
// 开启我们的线程池执行runnable
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;
}
上面是纯源码分析不是很好理解,我来转换一下思维给大家写一份伪代码
Observable.just("Hello world")
.subscribeOn(Schedulers.io())
.subscribe(result -> {
})
可以直接等价于
Schedulers.io().createWorker().schedule {
// 执行在 io线程中
Observable.just("Hello world").subscribe()
}
看完这个伪代码大家是不是一目了然,同理observerOn的分析是一样的
2.observerOn(AndroidSchedules.mainThread()) 会执行到ObservableObserveOn中去
@Override
protected void subscribeActual(Observer<? super T> observer) {
if (scheduler instanceof TrampolineScheduler) {
source.subscribe(observer);
} else {
// 这个Worker 就是我们 HandlerScheduler.HandlerWorker
Scheduler.Worker w = scheduler.createWorker();
source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
}
}
ObserveOnObserver 对象执行schedule 方法的时候 最终就会执行到 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) {
// 将run 进行保证
ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);
// 创建一个meesage对象
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);
}
// 发送message消息
handler.sendMessageDelayed(message, unit.toMillis(delay));
// Re-check disposed state for removing in case we were racing a call to dispose().
// 如果中断了就移除该message
if (disposed) {
handler.removeCallbacks(scheduled);
return Disposables.disposed();
}
return scheduled;
}
我们再写一个伪代码
Observable.just("Hello world")
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
});
我们可以等价于
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
String s = (String) msg.obj;
}
};
Message message = Message.obtain();
message.obj = "Hello world";
handler.sendMessage(message);
总结
1.如果当subscribeOn多次调用的时候 只有最上端的线程切换才是有效的,因为subscribeOn是改变上游 的线程, observerOn多次调用的时候 只有最下端的线程切换才有效,因为observerOn是改变下游线程调度的,就是最近原则最靠近那个卡片就生效
2.多次切换只有一次生效