在RxJava的使用过程中,通常会用subscribeOn来指定可观察者Observable的线程,用observerOn来指定观察者Observer的线程。本文将分析RxJava2.x的线程切换原理
先来看一个例子
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Integer> e) throws Exception {
mRxOperatorsText.append("Observable emit 1" + "\n");
Log.d(TAG, "Observable emit 1" + "\n");
e.onNext(1);
mRxOperatorsText.append("Observable emit 2" + "\n");
Log.d(TAG, "Observable emit 2" + "\n");
e.onNext(2);
mRxOperatorsText.append("Observable emit 3" + "\n");
Log.d(TAG, "Observable emit 3" + "\n");
e.onNext(3);
//e.onComplete();
mRxOperatorsText.append("Observable emit 4" + "\n");
Log.d(TAG, "Observable emit 4" + "\n" );
e.onNext(4);
Log.d(TAG, "subscribe: ThreadName="+Thread.currentThread().getName());
}
}).subscribeOn(Schedulers.io()) //将发射者切换到io线程
.observeOn(AndroidSchedulers.mainThread()) //将接收者切换到主线程
.subscribe(new Observer<Integer>() {
private int i;
private Disposable mDisposable;
@Override
public void onSubscribe(@NonNull Disposable d) {
mRxOperatorsText.append("onSubscribe : " + d.isDisposed() + "\n");
Log.d(TAG, "onSubscribe : " + d.isDisposed() + "\n" );
mDisposable = d;
Log.d(TAG, "onSubscribe: ThreadName="+Thread.currentThread().getName());
}
@Override
public void onNext(@NonNull Integer integer) {
mRxOperatorsText.append("onNext : value : " + integer + "\n");
Log.d(TAG, "onNext : value : " + integer + "\n" );
Log.d(TAG, "onNext: ThreadName="+Thread.currentThread().getName());
i++;
if (i == 2) {
// 在RxJava 2.x 中,新增的Disposable可以做到切断的操作,让Observer观察者不再接收上游事件
mDisposable.dispose();
mRxOperatorsText.append("onNext : isDisposable : " + mDisposable.isDisposed() + "\n");
Log.d(TAG, "onNext : isDisposable : " + mDisposable.isDisposed() + "\n");
}
}
@Override
public void onError(@NonNull Throwable e) {
mRxOperatorsText.append("onError : value : " + e.getMessage() + "\n");
Log.d(TAG, "onError : value : " + e.getMessage() + "\n" );
}
@Override
public void onComplete() {
mRxOperatorsText.append("onComplete" + "\n");
Log.d(TAG, "onComplete" + "\n" );
}
});
输出结果如下
如果不切换线程则输出结果如下
切换线程的情况下, 可以指定Observable的操作在IO线程中执行,Observer操作在主线程中执行,如果不切换线程的话,则默认都是主线程
在讲RxJava的线程切换之前,先来梳理一下RxJava框架的整人执行流程是怎么样的
一、RxJava的架构流程分析
下面是我画的一个RxJava的流程图,可能不是很严谨,但这样的流程图对源码的阅读和理解有很大的帮助
1.首先是一个原始可观察者,它持有一个数据发射源,然后生成第二级、第三级……第n级等一系列可观察者,每个可观察者都持有上一级可观察者的引用,每个可观察者里面都可以对上一级可观察者进行相关处理,当然也可以只有一个可观察者即原始可观察者
2.当完成可观察者的生成之后,就开始执行了,从生成的最后一个可观察者开始,依次执行每个可观察者的相关处理,同时还会从一个原始观察者开始生成一系列的观察者
3.当程序执行到原始可观察者的时候,就会触发数据发射源发射数据,然后第n级观察者就首先收到数据,同样是依次执行所有的观察者,每个观察者都可以对接收到的数据进行处理,然后传给它的下一级观察者,最后到原始观察者接收到数据,从而结束整个执行
下面我们就从源码的角度来看一下RxJava的线程切换原理
二:Observable的线程切换原理
从方法subscribeOn(Schedulers.io())入手
public final Observable<T> subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
}
方法接收一个Scheduler参数,按理解这是一个线程调度器,返回一个ObservableSubscribeOn对象,这是一个可观察者,可见这里创建了一个新的可观察者,注意构造方法第一个参数传递this,即当前Observable本身,可知新的可观察者持有了上一个可观察者的引用
来看一下ObservableSubscribeOn类
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> s) {
//创建新的观察者
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
//作为Disposable传参
s.onSubscribe(parent);
//利用scheduler.scheduleDirect(new SubscribeTask(parent))进行线程切换
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
...
}
在新的可观察者里面,其中subscribeActual方法是执行当前可观察者的的意思,也是我们重点关注的方法,里面会生成一个新的观察者,然后通过线程调度器scheduler切换下一个可观察者的执行线程,先来看看任务类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,同时把新的观察者传参过去,这就实现了可观察者的线程切换
下面就来看看这个SubscribeTask是如何在指定线程中执行的,scheduleDirect是零延时执行的意思
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 实现了Disposable接口
DisposeTask task = new DisposeTask(decoratedRun, w);
//通过Worker来执行
w.schedule(task, delay, unit);
return task;
}
createWorker是一个抽象方法,因为这里的线程调度器是Schedulers.io(),所以找到了IoScheduler,来看看它的createWorker方法
public Worker createWorker() {
return new EventLoopWorker(pool.get());
}
里面关于线程的调度有点复杂,主要是通过线程池来执行任务的,细节就不研究了,来看EventLoopWorker的schdule方法
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);
}
先判断任务有没有被中断,最终由threadWorker来执行任务
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;
}
三、Observer的线程切换原理
从上面得知,可观察者的线程切换,是通过将下一级的可观察者的执行放入到指定线程中来实现的;所以我们可以猜想,观察者的线程切换,应该也是通过将下一级的观察者的执行放入到指定线程中来实现的
下面从observerOn方法开始
public final Observable<T> observeOn(Scheduler scheduler) {
return observeOn(scheduler, false, bufferSize());
}
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));
}
可以看到,返回一个ObservableObserverOn实例,同样是生成一个新的可观察者
public ObservableObserveOn(ObservableSource<T> source, Scheduler scheduler, boolean delayError, int bufferSize) {
super(source);
this.scheduler = scheduler;
this.delayError = delayError;
this.bufferSize = bufferSize;
}
其中Scheduler为observerOn方法中传递的线程调度器,我们还是来关注可观察者的执行方法subscribeActual,一般会在这个方法中创建一个新的观察者
@Override
protected void subscribeActual(Observer<? super T> observer) {
if (scheduler instanceof TrampolineScheduler) {
source.subscribe(observer);
} else {
//这个worker来切换线程
Scheduler.Worker w = scheduler.createWorker();
source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
}
}
可以看到,在执行下一个可观察者的subscribe方法的时候,传入一个新的观察者ObserveOnObserver,这个新的观察者就是用来切换线程的,来看看它是怎么切换线程的,分析其中的onNext方法
public void onNext(T t) {
if (done) {
return;
}
if (sourceMode != QueueDisposable.ASYNC) {
//存入队列
queue.offer(t);
}
schedule();
}
@Override
public void onError(Throwable t) {
if (done) {
RxJavaPlugins.onError(t);
return;
}
error = t;
done = true;
schedule();
}
@Override
public void onComplete() {
if (done) {
return;
}
done = true;
schedule();
}
done代表工作有没有完成的意思,在onNext方法中先将当前观察者收到的数据保存到一个队列中,然后调用schedule方法
void schedule() {
if (getAndIncrement() == 0) {
worker.schedule(this);
}
}
同样,worker是我们在observerOn中指定的线程调度器,传参this,所以去看看类中run方法的实现
@Override
public void run() {
if (outputFused) {
drainFused();
} else {
//执行这里
drainNormal();
}
}
这里就是线程切换的关键了
void drainNormal() {
int missed = 1;
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 {
//从队列中取出接收到的数据T
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;
}
}
}
观察者线程的切换,首先是将当前观察者接收到的数据保存在一个队列中,然后在我们指定的线程内,从队列中的取出数据,传给下一个观察者,从而实现了线程的切换
到这里,我们理解了RxJava的线程切换原理,下一篇将分析有关变换操作符的原理