阅读 188

Rxjava2 线程切换流程解读

在介绍线程切换原理之前,介绍一下在 Rxjava2 中如何进行线程切换

祭出官网图

1.png

有两个操作符来决定如何切换线程:

  • subscribeOn 指定上游 subscribe() 所发生的线程
  • observeOn 指定 Subscriber 所运行在的线程

说人话就是,subscribeOn 决定从事件源发过来的线程,而 observeOn 决定每调用一次它之后的订阅发生在哪个线程里。

Single.subscribeOn() 事件流订阅

Single
		.just(1L)
    .subscribeOn(Schedulers.io())
复制代码

点进去看 subscribeOn 源码,跳过钩子调用后,ObservableSubscribeOn 为其实现类

@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)));
}
复制代码

内部实现为创建一个 SubscribeOnObserver 来传给下游订阅者。

同时,调用调度器的 scheduleDirect 来切线程,SubscribeTask 实现了 Runnable

 static final class SubscribeOnObserver<T>
    extends AtomicReference<Disposable>
    implements SingleObserver<T>, Disposable, Runnable {
    @Override
    public void run() {
        source.subscribe(this);
    }
}
复制代码

在 run 方法中执行订阅过程,这样以来整个对于上游的订阅发生在调度器所在线程。

如图所示:

2.png

操作符线程订阅

再增加一个操作符,查看执行所在线程情况。

我们用白线来指代调用Rxjava所处的原有线程,

用紫色代表使用 SubscribeOn 指定订阅的线程

3.png

可以看出经过 SubscribeOn 指定订阅所在的线程之后,从上流过来的数据至下流接受处理数据并返回订阅器,都会执行在我们执行的线程当中。

重复 SubscribeOn 线程切换

如果我们有多个 SubscribeOn ,那么线程会如何切换呢 ?

用紫色箭头代表代码中第一个指定的线程,绿色箭头表示后续指定的线程,白色箭头代表调用 Rxjava 原有所处线程。

4.png

虽然最后指定的 SubscribeOn 距离真实的 Observer 比较近,由于先进行了线程切换,等到遇到离上游最近的 SingleSubscribeOn 时,它会将线程再重新切换到它所指定的 Schedule。

从源码角度也可以看出,无论我们指定多少次 subscribeOn,最终有效的是最先指定的那个 subscribeOn。

Dispose 原理

public final class SingleSubscribeOn<T> extends Single<T> {
	@Override
	protected void subscribeActual(final SingleObserver<? super T> observer) {
	    final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(observer, source);
	    Disposable f = scheduler.scheduleDirect(parent);
	    parent.task.replace(f);
	}
}

@Override
public void onSubscribe(Disposable d) {
    DisposableHelper.setOnce(this, d);
}

@Override
public void dispose() {
    DisposableHelper.dispose(this);
    task.dispose();
}
复制代码

在调用 dispose 时,SingleSubscribeOn 会调用两块

  1. 上游 onSubscribe 传入的 Disposable
  2. 切换线程的 Disposable

目的,通知上游不要继续发数据,本身切换线程的操作也停止。

Single.observeOn 线程切换

Rxjava 中还有一个常用来线程切换的操作符

ObserveOn 指定线程后,在此之后做订阅的 observer 的 onSuccess、onError 都会被切换到指定线程。

我们如下使用 ObserveOn

Single.just(1)
    .observeOn(AndroidSchedulers.mainThread())
复制代码

查看源码,最终实现是在 SingleObserveOn 类中,查看其 subscribeActual

@Override
protected void subscribeActual(final SingleObserver<? super T> observer) {
    source.subscribe(new ObserveOnSingleObserver<T>(observer, scheduler));
}
复制代码

实际实现在 ObserveOnSingleObserver 中进行包装。

static final class ObserveOnSingleObserver<T> extends AtomicReference<Disposable>
implements SingleObserver<T>, Disposable, Runnable {
    @Override
    public void onSubscribe(Disposable d) {
        if (DisposableHelper.setOnce(this, d)) {
            downstream.onSubscribe(this);
        }
    }
    @Override
    public void onSuccess(T value) {
        this.value = value;
        Disposable d = scheduler.scheduleDirect(this);
        DisposableHelper.replace(this, d);
    }
    @Override
    public void onError(Throwable e) {
        this.error = e;
        Disposable d = scheduler.scheduleDirect(this);
        DisposableHelper.replace(this, d);
    }
    @Override
    public void run() {
        Throwable ex = error;
        if (ex != null) {
            downstream.onError(ex);
        } else {
            downstream.onSuccess(value);
        }
    }
    @Override
    public void dispose() {
        DisposableHelper.dispose(this);
    }
}
复制代码

这个代理 observer 和 subscribeOn 操作符的区别在于,subscribeOn 操作符是在接收到订阅时直接进行切换线程操作。而 ObserveOn 则是在 onSuccess、OnError 中进行线程的切换,其不影响上游的数据处理线程,只影响下游接收订阅结果所处的线程。

我们用白色箭头指代调用 Rxjava 订阅所处线程,红色箭头表示 ObserveOn 中指定要切换的线程。

5.png

从图中也可以明显看出,ObserveOn 只影响后续订阅结果的线程,不影响上游处理数据的线程

组合 Map 切换线程

Single.just(1)
    .observeOn(AndroidSchedulers.mainThread())
    .map(object : Function<Int, Int> {
        override fun apply(t: Int): Int {
            return t
        }
    })
复制代码

其运行实际线程如下图所示

用白色箭头指代调用 Rxjava 订阅所处线程,红色箭头表示 ObserveOn 中指定要切换的线程。

6.png

可以看出组合 map 之后,整个 ObserveOn 的下游 Map 订阅、用户订阅结果线程都被切换成了ObserveOn 内指定线程

结合 SubscribeOn 切换线程

Single.just(1)
    .observeOn(Schedulers.computation())
    .map(object : Function<Int, Int> {
        override fun apply(t: Int): Int {
            return t
        }
    })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeOn(Schedulers.io())
    .subscribe(object : SingleObserver<Int?> {
复制代码

demo 中我们指定 map 运行在 computation 调度器中,用户订阅在安卓主线程。

用白色箭头指代调用 Rxjava 订阅所处线程,紫色箭头表示希望数据源执行的线程,蓝色箭头代表,在 Map 操作符中的监听,红色箭头表示用户定义在 ObserveOn 中指定要切换的线程。

7.png

我们可以看出,在 ObserveOnSubscribeOn 进行组合时,具体 SubscribeOn 所处的定义位置,其实并不影响实际上订阅线程,以及其他订阅需要执行在的线程。具体每一个操作符如果需要切线程,只需要在操作符前增加一个 ObserveOn 操作符即可。

Dispose 原理

@Override
public void onSubscribe(Disposable d) {
    if (DisposableHelper.setOnce(this, d)) {
        downstream.onSubscribe(this);
    }
}

@Override
public void onSuccess(T value) {
    this.value = value;
    Disposable d = scheduler.scheduleDirect(this);
    DisposableHelper.replace(this, d);
}
@Override
public void onError(Throwable e) {
    this.error = e;
    Disposable d = scheduler.scheduleDirect(this);
    DisposableHelper.replace(this, d);
}

@Override
public void dispose() {
    DisposableHelper.dispose(this);
}
复制代码

ObserveOn 中的 dispose 分为几种情况

  1. 上游数据尚未发送到 onSuccess 、OnError,此时取消的是上游
  2. 上游已经发送到 ObserveOn 中,正在切线程,DisposableHelper.replace(this, d);,此时取消的就是切线程的操作。

到此位置,有关 SubscribeOn 以及 ObserveOn 如何进行线程切换的原理和流程走向就介绍完了。

做一些总结:

  1. SubscribeOn 会切换从数据源发送的线程
  2. SubscribeOn 所处的位置与其他操作符位置无关,具有两个 SubscribeOn 操作符时,以最顶层的 SubscribeOn 操作符为准
  3. 线程切换中没有 ObserveOn 时,整个数据流的线程都会被切换成 SubscribeOn 指定的线程,在有 ObserveOn 的线程时,数据流会影响操作符之后的传递线程。

在之后的文章中会对 Schedules 的内部实现逻辑进行更细致的介绍。

文章分类
Android
文章标签