RxJava 基本原理

响应式编程

响应式编程(Reactive Programming)是一种编程范式,它专注于数据流和变化传播。在响应式编程中,程序的整体结构是由数据流的变化来驱动的,而不是通过显式的命令式编程指令。主要特点包括:

  1. 数据流: 在响应式编程中,程序的数据是以流的形式进行传递和处理的。这些数据流可以是同步的也可以是异步的,它们可以是单向的,也可以是双向的。

  2. 事件驱动: 程序的状态和行为是由事件的发生和响应来驱动的。事件可以是用户输入、网络请求、定时器触发等等。

  3. 响应式变换: 在数据流中,通过应用各种操作符来对数据进行变换和处理,以产生新的数据流。这些操作符可以是过滤、映射、聚合、合并等等。

  4. 响应式组合: 可以通过组合多个数据流和操作符来创建更复杂的数据流,以实现更复杂的功能。

  5. 异步编程: 响应式编程通常与异步编程密切相关,因为数据流可能涉及到异步操作,如异步 I/O、网络请求等。

响应式编程的优势包括:

  1. 简洁性: 响应式编程使得代码更加简洁、清晰,减少了传统编程中的回调嵌套和临时变量等问题。

  2. 可读性: 响应式编程使得程序的逻辑更加清晰,代码更易于理解和维护。

  3. 可组合性: 响应式编程通过数据流的组合和操作符的组合,使得程序更容易扩展和重用。

  4. 响应性: 响应式编程使得程序能够更快地响应外部事件和用户操作,提高了用户体验。

  5. 异步性: 响应式编程使得程序能够更好地处理异步操作,提高了系统的并发能力和性能。

总之,响应式编程通过数据流和事件驱动的方式,使得程序更加简洁、清晰、可读、可组合,并且能够更好地处理异步操作,提高了程序的响应性和性能。

为什么要使用 RxJava

  • 轻量级框架 < 1M
  • 符合响应式编程,着重于数据流和变化的传播,以及对这些变化的响应
  • 基于观察者模式,统一了异步任务的回调接口,方便链式调用和操作符扩展
  • 通过 Scheduler / Worker 可以方便的进行线程切换
  • 在观察者和被观察者之间可以通过操作符完成时间和空间的重组
    • 通过操作符可以对事件进行空间重组,使得观察者的逻辑简单而直接,不需要关心数据从何而来,从而使观察者的逻辑较为稳定
      • startWith 操作符可以增加网络缓存逻辑
      • distinctUntilChanged 操作符可以避免多次刷新UI
      • concatWith 可以实现任务链式执行,进教室处理逻辑( fetchToken + enterRoom)
      • mergeDelayError 可以合并多个 Observable(进教室逻辑会收集各核心模块初始化的结果,来决定进教室是否成功)
      • RecyclerView 需要多次网络请求获取不同类型的数据时,首先通过接口获取要展示的数据类型,然后通过 map 方法返回 Observable 类型的 List(Observable 需要 startWith 一个空的数据集合,主要是配合 combineLatest 使用),进而通过 flatMap 映射为 Observable.combineLatest
    • 通过操作符对事件进行时间重组
      • debounce 可以实现连续点赞只上报最后的点赞状态
      • timeout 可以实现接口超时逻辑
      • zipWith 配合 startWith 可以同时拿到点赞状态改变前后的信息,然后只有在点赞状态改变时才上报
      • zipWith 配合 skip 可以实现双击判断

操作符实现思路

  • ObservableXXX 的构造方法会把上层 Observable 保存到 source 变量中
  • ObservableXXX 的 subscribeActual 一般会生成一个内部的 Observer,用来订阅上层 Observable,并代理 ObservableXXX 的特殊处理逻辑
  • 复杂的 ObservableXXX 会同时保存 upstream 和 downstream,用于传递事件并处理 disposable 逻辑,还有一些 ObservableXXX 会通过 DisposableHelper.replace 方法来统一上下游的 Disposable 实例

操作符分类

Creating Observables

create自定义发射逻辑
just发射若干个事件(通过参数个数区分)
range发射连续的数列
fromfromArray(发射固定的事件)、fromCallable(每次订阅都会根据supplier获取最新的事件)
defer每次订阅都会获取最新的 Observable
interval固定间隔发送长整型事件(递增)
timer延迟一定时间后发送长整型事件(0L)
repeat重复订阅,在收到 onComplete 事件后会重新订阅 ObservableSource

Transforming Observables

buffer将原始事件整合为固定大小的 List 后发送
window将原始事件整合到多个不同的 ObservableSource,每个 ObservableSource 依次发送固定数量的原始事件
groupby将原始事件整合到多个不同的 ObservableSource,每个 ObservableSource 发送不同组的数据
flatMap将普通事件转换为 ObservableSource 类型的事件,按发射时间整合各 ObservableSource 的发射顺序
concatMap将普通事件转换为 ObservableSource 类型的事件,顺序整合各 ObservableSource 发射的数据
switchMap将普通事件转换为 ObservableSource 类型的事件,如果后面的 ObservableSource 开始发射数据,会自动终止前面的 ObservableSource
map数据转换(普通事件 -> 普通事件)
cast类型强制转换,内部通过 map 功能实现类型转换功能
scan累加所有原始事件,并依次输出对应的结果

Filtering

debounce收到事件后延时一定时间后向下游发射,延时期间如果收到新的事件,则丢弃原有事件,并重新计时
sample固定时间采样,如果一段时间间隔内没有新的事件则不发送,否则发送最新的数据
distinct过滤重复事件(和历史事件比较)
distinctUntilChanged过滤重复事件(和前一个事件比较)
elementAt返回 Maybe 类型
filter过滤符合条件的事件
first只发射第一个事件,如果收到 Complete 前没有收到事件,则发射 Default 数据
last只发射最后一个事件,如果收到 Complete 前没有收到事件,则发射 Default 数据
skip / take跳过固定数量的事件 / take 固定数量的事件

Combining

combineLatest在每个数据源都发射了数据的前提下,任何一个数据源发射数据就会触发新的数据发射(新数据根据 combine 方法计算)
zip一对一整合多个数据源发射的数据,遇到一个结束则结束
join整合两个数据源,每个数据源发射的数据都会对应一个过期时间。其中一个数据源发射数据时,如果另外一个数据源没有未过期的数据,则不发射新的数据
merge根据发射时间将多个发射源整合为一个,不存在数据转换逻辑,其中一个终止则终止
mergeDelayError根据发射时间将多个发射源整合为一个,不存在数据转换逻辑,延迟发射 Error 事件
switchOnNext将发射ObservableSource事件的数据源转换为发射普通数据的数据源,新的数据源会发射当前最新ObservableSource发射的数据

ErrorHandling

OnErrorReturn遇到错误返回预设的值,然后正常终止
OnErrorResumeNext遇到 Error 后订阅另外一个 Observable 并发射数据
OnExceptionResumeNext遇到 Exception 后订阅另外一个 Observable 并发射数据
retryWhen遇到错误后,会根据参数 Observable 发射数据的时机来重新订阅原始数据源

Utility

Delay / DelaySubscription延迟发射 / 延迟订阅
SubscribeOn / ObserveOn切换发射 / 订阅线程
TimeInterval发射事件序列的时间间隔
TimeStamp发射事件对应的时间戳信息
Timeout固定时间未发送事件,则发送 Error 事件
doOn(Each、Subscribe、Next、Error、Complete)
doOnTerminatedoOnError + doOnComplete
doOnDispose
doFinallydoOnTerminate + doOnDispose

Conditional and Boolean

all判断所有事件是否满足条件,返回 Single
amb抢占式,始终发射第一个开始发射事件的 ObservableSource
contains / isEmpty是否包含/是否为空,对应Single
DefaultIfEmpty如果未发送数据,则发送Default数据
SequenceEqual一次判断多个数据源发射的数据,如果不相等立即返回false
SkipUntil / SkipWhile
TakeUntil / TakeWhile

Aggregate

Concat按顺序连接多个 Observable
Count计算发射了多少个事件,返回 Single
Reduce和 scan 处理逻辑相同,不过只输出最终结果
Collect和 reduce 处理逻辑相同,只不过最终输出的是集合类型

Scheduler&Worker 接口实现

@Suppress("unused")
fun CoroutineDispatcher.asScheduler(scope: CoroutineScope = GlobalScope)
                                : Scheduler = CoroutineScheduler(this, scope)

// implement createWorker
internal class CoroutineScheduler(
    private val dispatcher: CoroutineDispatcher,
    private val scope: CoroutineScope
) : Scheduler() {
    override fun createWorker(): Worker = CoroutineWorker(dispatcher, scope)
}


internal class CoroutineWorker(
    private val dispatcher: CoroutineDispatcher,
    private val scope: CoroutineScope
) : Scheduler.Worker() {

    private val supervisorJob = SupervisorJob(scope.coroutineContext[Job])

    @Volatile
    private var isDisposed = false

    override fun isDisposed(): Boolean = isDisposed

    override fun dispose() {
        if (!isDisposed) {
            isDisposed = true
            supervisorJob.cancel()
        }
    }

    override fun schedule(run: Runnable, delay: Long, unit: TimeUnit): Disposable {
        if (isDisposed || supervisorJob.isCancelled) {
            return EmptyDisposable.INSTANCE
        }

        val decoratedRun = RxJavaPlugins.onSchedule(run)
        val job = scope.launch(supervisorJob) {
            withContext(dispatcher) {
                if (delay > 0L) delay(unit.toMillis(delay))
                decoratedRun.run()
            }
        }

        return JobDisposable(job)
    }
}

internal class JobDisposable(private val job: Job) : Disposable {
    @Volatile
    private var isDisposed = false

    override fun isDisposed(): Boolean = isDisposed

    override fun dispose() {
        if (!isDisposed) {
            isDisposed = true
            job.cancel()
        }
    }
}

发射器类型

类型

描述

Observable

能够发射0或n个数据,并以成功或错误事件终止(onNext、onComplete、onError)

Flowable

能够发射0或n个数据,并以成功或错误事件终止;支持Backpressure,可以控制数据源发射的速度(ERROR、BUFFER、DROP、LATEST)

Single

只发射单个数据或错误事件(onSuccess、onError)

Completable

不发射数据,只处理onComplete和onError事件(onComplete、onError)

Maybe

能够发射0或1个数据,要么成功,要么失败((onSuccess、onComplete)互斥关系、onError)

Subject

Subject 同时实现了 Observer 和 Observable 接口,具体有四个实现类

ReplaySubject

该 Subject 会接收数据,当被订阅时,将所有接收到的数据全部发送给订阅者

BehaviorSubject

当 Observer 订阅了一个 BehaviorSubject,它一开始就会释放 Observable 最近释放的一个数据对象,当还没有任何数据释放时,它则是一个默认值。接下来就会释放 Observable 释放的所有数据。如果 Observable 因异常终止,BehaviorSubject 将不会向后续的 Observer  释放数据,但是会向 Observer 传递一个异常通知

PublishSubject

PublishSubject 仅会向 Observer 释放在订阅之后 Observable 释放的数据

AsyncSubject

AsyncSubject 仅释放 Observable 释放的最后一个数据,并且仅在 Observable 完成之后。然而如果当 Observable 因为异常而终止,AsyncSubject 将不会释放任何数据,但是会向 Observer 传递一个异常通知

FlatMap、ConcatMap、ConcatMapEager区别

  • FlatMap 不能保证各个 Observable 发射数据的顺序
  • ConcatMap 可以保证各个 Observable 发射数据的顺序,由于是顺序订阅,效率较低
  • ConcatMapEager 采用缓存策略,来保证各个 Observable 发射数据的顺序,需要考虑内存占用问题

经典操作符赏析

Observable.subscribeOn

  1. 该操作符生成并返回 ObservableSubscribeOn 类
  2. ObservableSubscribeOn 构造方法中保存了 ObservableSource 和 Scheduler
  3. subscribeActual 方法会创建代理 SubscribeOnObserver:用于向原始 Observer 透传事件,并作为 Disposable 返回给原始 Observer
  4. SubscribeOnObserver 内部同时处理两个 Disposable:schedule 方法返回的 Disposable(保存在AtomicReference),以及订阅 source 返回的 Disposable(保存在 upstream 中)
  5. 其中通过执行 scheduler.scheduleDirect 完成线程切换并执行 source.subscribe(parent)
public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
    final Scheduler scheduler;

    public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
        super(source);
        this.scheduler = scheduler;
    }

    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)));
    }
}

static final class SubscribeOnObserver<T> extends AtomicReference<Disposable> implements Observer<T>, Disposable {      
    final AtomicReference<Disposable> upstream;

    SubscribeOnObserver(Observer<? super T> downstream) {
        this.downstream = downstream;
        this.upstream = new AtomicReference<Disposable>();
    }

    @Override
    public void onSubscribe(Disposable d) {
        DisposableHelper.setOnce(this.upstream, d);
    }
        
    @Override
    public void onNext(T t) {
        downstream.onNext(t);
    }

    @Override
    public void dispose() {
        DisposableHelper.dispose(upstream);
        DisposableHelper.dispose(this);
    }
}

final class SubscribeTask implements Runnable {
    private final SubscribeOnObserver<T> parent;

    SubscribeTask(SubscribeOnObserver<T> parent) {
        this.parent = parent;
    }

    @Override
    public void run() {
        source.subscribe(parent);
    }
}

Observable.observeOn

  1. 该操作符生成并返回ObservableObserverOn类
  2. ObservableSubscribeOn构造方法中保存了ObservableSource和Scheduler
  3. subscribeActual方法会创建代理Observer(ObserveOnObserver):内部在onNext、onError、onComplete方法中会把事件存储在queue中,并调用worker的schedule方法,run方法中会循环读取queue中的任务发送给下一层的Observer,并处理delayError相关逻辑
public final class ObservableObserveOn<T> extends AbstractObservableWithUpstream<T, T> {
    public ObservableObserveOn(ObservableSource<T> source, Scheduler scheduler, boolean delayError, int bufferSize) {
        super(source);
        this.scheduler = scheduler;
        this.delayError = delayError;
        this.bufferSize = bufferSize;
    }

    @Override
    protected void subscribeActual(Observer<? super T> observer) {
        Scheduler.Worker w = scheduler.createWorker();
        source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
    }
}

    static final class ObserveOnObserver<T> extends BasicIntQueueDisposable<T> implements Observer<T>, Runnable {

        @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);
            }
        }

        @Override
        public void run() {
            if (outputFused) {
                drainFused();
            } else {
                drainNormal();
            }
        }
    }

Observable.map

  1. 该操作符生成并返回ObservableMap类
  2. ObservableMap构造方法中保存了ObservableSource和function
  3. subscribeActual方法会创建代理Observer(MapObserver),内部onNext、onComplete、onError方法会直接执行map-function,然后也不会切换线程
public final class ObservableMap<T, U> extends AbstractObservableWithUpstream<T, U> {
    final Function<? super T, ? extends U> function;

    public ObservableMap(ObservableSource<T> source, Function<? super T, ? extends U> function) {
        super(source);
        this.function = function;
    }

    @Override
    public void subscribeActual(Observer<? super U> t) {
        source.subscribe(new MapObserver<T, U>(t, function));
    }
}

Observable.flatMap

  1. 该操作符生成并返回ObservableFlatMap类
  2. subscribeActual方法会创建MergeObserver(在MergeObserver构造方法中保存原始订阅者),观察数据源,并保存Disposable到upstream
  3. MergeObserver#onNext方法中调用mapper.apply,创建InnerObserver观察新的数据源
  4. InnerObserver#onNext方法调用parent(MergeObserver)#tryEmit方法将订阅数据返回给MergeObserver,并最终发送给原始订阅者
  5. 用户通过调用MergeObserver来dispose掉整个链路(upstream、innerObserver数组)
public final class ObservableFlatMap<T, U> extends AbstractObservableWithUpstream<T, U> {
    final Function<? super T, ? extends ObservableSource<? extends U>> mapper;
    final boolean delayErrors;
    final int maxConcurrency;
    final int bufferSize;

    public ObservableFlatMap(ObservableSource<T> source,
            Function<? super T, ? extends ObservableSource<? extends U>> mapper,
            boolean delayErrors, int maxConcurrency, int bufferSize) {
        super(source);
        this.mapper = mapper;
        this.delayErrors = delayErrors;
        this.maxConcurrency = maxConcurrency;
        this.bufferSize = bufferSize;
    }

    @Override
    public void subscribeActual(Observer<? super U> t) {
        source.subscribe(new MergeObserver<T, U>(t, mapper, delayErrors, maxConcurrency, bufferSize));
    }

    static final class MergeObserver<T, U> extends AtomicInteger implements Disposable, Observer<T>

        MergeObserver(Observer<? super U> actual,
                Function<? super T, ? extends ObservableSource<? extends U>> mapper,
                boolean delayErrors, int maxConcurrency, int bufferSize) {
            this.downstream = actual;
            this.mapper = mapper;
            this.delayErrors = delayErrors;
            this.maxConcurrency = maxConcurrency;
            this.bufferSize = bufferSize;
            if (maxConcurrency != Integer.MAX_VALUE) {
                sources = new ArrayDeque<ObservableSource<? extends U>>(maxConcurrency);
            }
            this.observers = new AtomicReference<InnerObserver<?, ?>[]>(EMPTY);
        }

        // 给真正的订阅者返回的Disposable为MergeObserver,在MergeObserver中保存上层订阅的Disposable到upstream
        @Override
        public void onSubscribe(Disposable d) {
            if (DisposableHelper.validate(this.upstream, d)) {
                this.upstream = d;
                downstream.onSubscribe(this);
            }
        }

        // 每次onNext都会通过mapper来订阅新的数据源
        @Override
        public void onNext(T t) {
            ObservableSource<? extends U> p = mapper.apply(t)
            InnerObserver<T, U> inner = new InnerObserver<T, U>(this, uniqueId++);
            if (addInner(inner)) {
                 p.subscribe(inner);
            }
        }

        // 保存InnerObserver,用于Dispose
        boolean addInner(InnerObserver<T, U> inner) {
            for (;;) {
                InnerObserver<?, ?>[] a = observers.get();
                int n = a.length;
                InnerObserver<?, ?>[] b = new InnerObserver[n + 1];
                System.arraycopy(a, 0, b, 0, n);
                b[n] = inner;
                if (observers.compareAndSet(a, b)) {
                    return true;
                }
            }
        }

        boolean disposeAll() {
            upstream.dispose();
            InnerObserver<?, ?>[] a = observers.get();
            if (a != CANCELLED) {
                a = observers.getAndSet(CANCELLED);
                if (a != CANCELLED) {
                    for (InnerObserver<?, ?> inner : a) {
                        inner.dispose();
                    }
                    return true;
                }
            }
            return false;
        }
    }

    static final class InnerObserver<T, U>
                   extends AtomicReference<Disposable> implements Observer<U> {

        @Override
        public void onNext(U t) {
            if (fusionMode == QueueDisposable.NONE) {
                parent.tryEmit(t, this);
            } else {
                parent.drain();
            }
        }

        @Override
        public void onError(Throwable t) {
            if (parent.errors.addThrowable(t)) {
                if (!parent.delayErrors) {
                    parent.disposeAll();
                }
                done = true;
                parent.drain();
            } else {
                RxJavaPlugins.onError(t);
            }
        }
    }
}


FlatMap Demo

// 常规flatmap
    fun handleFlatMap() {
        Observable.create<Int> {
            Log.i("ebb1", Thread.currentThread().name)
            it.onNext(1)
            it.onNext(2)
            it.onComplete()
        }
            .flatMap {
                Observable.create<Float> { emitter ->
                    Log.i("ebb2", Thread.currentThread().name)
                    emitter.onNext(3.14f)
                    emitter.onComplete()
                }.subscribeOn(Schedulers.io())
            }
            .map {
                Log.i("ebb3", Thread.currentThread().name)
                it
            }
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .map {
                Log.i("ebb4", Thread.currentThread().name + " " + it)
            }
            .subscribe()
    }

//2021-06-22 15:21:12.434 I/ebb1: RxCachedThreadScheduler-1
//2021-06-22 15:21:12.439 I/ebb2: RxCachedThreadScheduler-3
//2021-06-22 15:21:12.439 I/ebb3: RxCachedThreadScheduler-3
//2021-06-22 15:21:12.440 I/ebb2: RxCachedThreadScheduler-2
//2021-06-22 15:21:12.440 I/ebb3: RxCachedThreadScheduler-2
//2021-06-22 15:21:12.502 I/ebb4: main 3.14
//2021-06-22 15:21:12.503 I/ebb4: main 3.14


//带异常的flatmap操作
//由于ObservableObserveOn向原始订阅者发送数据时会暂存到queue中,
//并通过Worker.schedule切换到线程,而且设置状态也是不切换线程的,
//这会有一定概率导致onNext状态丢失,如果不调用sleep,则订阅者基本不会收到onNext事件
    fun handleFlatMap() {
        Observable.create<Int> {
            Log.i("ebb1", Thread.currentThread().name)
            it.onNext(1)
            it.onNext(2)
            it.onComplete()
        }
            .flatMap {
                Observable.create<Float> { emitter ->
                    Log.i("ebb2", Thread.currentThread().name)
                    emitter.onNext(3.14f)
                    Thread.sleep(1000)
                    emitter.onError(Exception("error"))
                }
//                    .subscribeOn(Schedulers.io())
// 这里需要屏蔽flatmap内的线程切换,否则会有一定概率提示错误无法分发,具体原因没有细追
            }
            .map {
                Log.i("ebb3", Thread.currentThread().name)
                it
            }
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .map {
                Log.i("ebb4", Thread.currentThread().name + " " + it)
                it
            }
            .subscribe({
                Log.e("ebb5", "$it")
            },{
                Log.e("ebb5", it.message)
            })
    }


Observable.retryWhen

  1. 该操作符生成并返回ObservableRetryWhen类
  2. 构造方法中保存了Source和Handler
  3. subscribeActual方法中创建了一个PublishSubject,通过该Subject来对外通知错误信息,内部通过parent.inner来观察外部注入的Observable,并再次调用RepeatWhenObserver的subscribeNext方法
public final class ObservableRetryWhen<T> extends AbstractObservableWithUpstream<T, T> {

    final Function<? super Observable<Throwable>, ? extends ObservableSource<?>> handler;

    public ObservableRetryWhen(ObservableSource<T> source,
         Function<? super Observable<Throwable>, ? extends ObservableSource<?>> handler) {
        super(source);
        this.handler = handler;
    }

    @Override
    protected void subscribeActual(Observer<? super T> observer) {
        Subject<Throwable> signaller = PublishSubject.<Throwable>create().toSerialized();
        ObservableSource<?> other = handler.apply(signaller)
        RepeatWhenObserver<T> parent = new RepeatWhenObserver<T>(observer, signaller, source);
        observer.onSubscribe(parent);
        other.subscribe(parent.inner);
        parent.subscribeNext();
    }

    static final class RepeatWhenObserver<T> extends AtomicInteger implements Observer<T>, Disposable {

        void subscribeNext() {
            if (wip.getAndIncrement() == 0) {
                do {
                    if (isDisposed()) {
                        return;
                    }
                    if (!active) {
                        active = true;
                        source.subscribe(this);
                    }
                } while (wip.decrementAndGet() != 0);
            }
        }

        @Override
        public void onError(Throwable e) {
            DisposableHelper.replace(upstream, null);
            active = false;
            signaller.onNext(e);
        }

        final class InnerRepeatObserver extends AtomicReference<Disposable> implements Observer<Object> {

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

            @Override
            public void onNext(Object t) {
                innerNext();
            }

            @Override
            public void onError(Throwable e) {
                innerError(e);
            }

            @Override
            public void onComplete() {
                innerComplete();
            }
        }
    }
}

observeOn 作用于该操作符之后的操作符直到出现新的 observeOn 操作符

subscribeOn 作用于该操作符之前的 Observable 的创建操符作以及 doOnSubscribe 操作符 ,换句话说就是 doOnSubscribe 以及 Observable 的创建操作符总是被其之后最近的 subscribeOn 控制

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

little-sparrow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值