RxJava 二:RxJava 3 观察者模式(原理解析)

目录

1.RxJava 3

2.观察者模式组合

3.Flowable(数据源类一)

3.1.Flowable

3.2.BackpressureStrategy背压策略

3.3.响应式拉取(Flowable特有)

3.4.FlowableEmitter(数据发送器)

3.5.背压问题解决方案:(Flowable按需发送数据)

4.Observable(数据源类二)

4.1.Observable

4.2.ConnectableObservable

4.3.refObservable

4.4.autoObservable

5.Observer and Subscriber


1.RxJava 3

JavaDoc

http://reactivex.io/RxJava/3.x/javadoc/

https://github.com/ReactiveX/RxJava/wiki/What's-different-in-3.0

添加依赖

implementation 'io.reactivex.rxjava3:rxjava:3.x.y'

implementation 'io.reactivex.rxjava3:rxandroid:3.x.y'

Java 8(来源于官方文档)

由于Android运行时支持方面的落后,RxJava 长期基于Java 6 API。未来的 Android Studio 4中,一个叫做 desuging 的过程能够将许多Java 7和8的特性,透明地转换成与 Java 6兼容的特性。

So,RxJava3的baseline可以提高到 Java 8,并增加Java 8的官方支持,比如:Optional、Stream等,因此必须将项目的编译目标设置更改为 java8:

android {

    compileOptions {
        sourceCompatibility JavaVersion.
VERSION_1_8
       
targetCompatibility JavaVersion.VERSION_1_8
   
}

}

RxJava 3.0遵循Reactive-Streams specification规范。

2.观察者模式组合

在RxJava 3中,提供了五对观察者模式组合,每一对组合依靠其可调用的一系列函数的差异,具有各自的特点。

第一组:Observable(ObservableSource)/ Observer

一次可发送单条数据或者数据序列onNext,可发送完成通知onComplete或异常通知onError,不支持背压。

第二组:Publisher(Flowable)/ Subscriber(FlowableSubscriber)

第一组基础上进行改进,发送0到N个的数据(onNext),支持Reactive-Streams和背压,可发送完成通知onComplete或异常通知onError,但效率没有第一组高。

第三组:Single(SingleSource) / SingleObserver

第一组简化版,只能发送单条数据onSuccess,或者异常通知onError

第四组:Completable(CompletableSource)/ CompletableObserver

第一组简化版,不能发送数据,只发送完成通知onComplete或者异常通知onError

第五组:Maybe(MaybeSource) / MaybeObserver

第三,第四组的合并版,只能发送单条数据onSuccess和完成通知onComplete或者发送一条异常通知onError

3Flowable(数据源类一)

3.1.Flowable

Flowable是RxJava2.x中新增的,专门用于应对背压(Backpressure)问题,在Observable的基础上优化后的产物。

Observable ——> subscribe() ——> Observer

Flowable ——> subscribe() ——> Subscriber

后者是前者针对背压问题的优化产物,前者运行效率更高,后者支持背压问题处理,但逻辑更多,运行效率稍低,速度稍慢。

Flowable数据流程:Flowable ——> 异步缓存池(128)——> Subscriber

Flowable默认异步缓存队列大小为128。作用是缓存当前未来得及处理(Subscriber未接收,或者由于某种原因,Flowable未实际发送给Subscriber)的数据。也就是说随着Subscriber对数据的接收处理,缓存池也会随之做清理更新:去掉已经被Subscriber接收的,塞进Flowable新发送的。

注意:并不是Subscriber接收一条,便清理一条,而是存在一个延迟,等累积一段时间后统一清理一次。

所以:当生产者和消费者在相同的线程中;或者当生产者和消费者在不同线程中,但消费者消费数据的速度不低于生产者发送数据的速度,或者整条数据流中只有一条数据时 …… 使用Observable会更好。

Flowable

        .create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> e) throws Exception {
                System.out.println("发送----> 1");
                e.onNext(1);
                System.out.println("发送----> 2");
                e.onNext(2);
                System.out.println("发送----> 3");
                e.onNext(3);
                System.out.println("发送----> 完成");
                e.onComplete();
            }
        }, BackpressureStrategy.BUFFER) //指定背压处理策略:缓存
        .subscribeOn(Schedulers.newThread())
        .observeOn(Schedulers.newThread())
        .subscribe(new Subscriber<Integer>() {
            @Override
            public void onSubscribe(Subscription s) {
                s.request(Long.MAX_VALUE); // 响应式拉取
            }
            @Override
            public void onNext(Integer integer) {
                System.out.println("接收----> " + integer);
            }
            @Override
            public void onError(Throwable t) {}
            @Override
            public void onComplete() {
                System.out.println("接收----> 完成");
            }
        });

注意:

(1)BackpressureStrategy.BUFFER设置背压处理策略

(2)Subscription区别于Observer的Disposable

(3)发送器FlowableEmitter,不同于Observable的ObservableEmitter

3.2.BackpressureStrategy背压策略

Flowable本身适用于异步场景,内置默认上限128的异步缓存池,用于缓存当前未来得及处理(Subscriber未接收或者由于某种原因Flowable未实际发送给Subscriber)的数据。BackpressureStrategy的作用便是用来设置Flowable通过异步缓存池缓存数据的策略。

RxJava 3针对不同的五种策略(MISSING,ERROR,DROP,LATEST,BUFFER)内置了相应的数据发送器类,系统通过代理模式对五种数据发送器做了包装(FlowableCreate.java中实现)。

public void subscribeActual(Subscriber<? super T> t) {

    BaseEmitter<T> emitter;
    switch (backpressure) {
        case MISSING: {
            emitter = new MissingEmitter<T>(t);
            break;
        }
        case ERROR: {
            emitter = new ErrorAsyncEmitter<T>(t);
            break;
        }
        case DROP: {
            emitter = new DropAsyncEmitter<T>(t);
            break;
        }
        case LATEST: {
            emitter = new LatestAsyncEmitter<T>(t);
            break;
        }
        default: {
            emitter = new BufferAsyncEmitter<T>(t, bufferSize());
            break;
        }
    }
    t.onSubscribe(emitter);
    // ......
}

MISSING(MissingEmitter<T>)

策略:无特定背压处理策略,对数据无缓存和丢弃的处理,需结合创建背压操作符确定背压策略。

背压操作符:

onBackpressureBuffer() 对应BackpressureStrategy.BUFFER

onBackpressureDrop() 对应BackpressureStrategy.DROP

onBackpressureLatest() 对应BackpressureStrategy.LATEST

static final class MissingEmitter<T> extends FlowableCreate.BaseEmitter<T> {

    // ......
    @Override
    public void onNext(T t) {
        if (isCancelled()) {
            return;
        }
        if (t != null) {
            actual.onNext(t);
        } else {
            onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
            return;
        }
        for (; ; ) {
            long r = get();
            if (r == 0L || compareAndSet(r, r - 1)) {
                return;
            }
        }
    }
}

ERROR(ErrorAsyncEmitter<T>)

策略:Flowable异步缓存池超限(onOverflow()),直接扔异常MissingBackpressureException,缓存池上限128。

static final class ErrorAsyncEmitter<T> extends NoOverflowBaseAsyncEmitter<T> {

    private static final long serialVersionUID = 338953216916120960L;
    ErrorAsyncEmitter(Subscriber<? super T> actual) {
        super(actual);
    }
    @Override
    void onOverflow() {
        onError(new MissingBackpressureException("create: could not emit value due to lack of requests"));
    }
}

DROP(DropAsyncEmitter<T>)

策略:Flowable的异步缓存池满了,直接丢掉后续发送的数据。

static final class DropAsyncEmitter<T> extends NoOverflowBaseAsyncEmitter<T> {

    private static final long serialVersionUID = 8360058422307496563L;
    DropAsyncEmitter(Subscriber<? super T> actual) {
        super(actual);
    }
    @Override
    void onOverflow() {// nothing to do}
}

onOverflow()方法 do nothing!很明显处理逻辑在其父类NoOverflowBaseAsyncEmitter,查看代码逻辑,发现在调用actual.onNext(t);之前做了get() != 0的判断,意思就是说缓存超限,什么也不做,不超限才发送数据。也就是说如果Flowable的异步缓存池满了,会丢掉后续由上层发送的数据(FlowableEmitter<Integer> e onNext() 发送了,但是底层actual.onNext(t);实际并未调用)

@Override

public final void onNext(T t) {
    if (isCancelled()) {
        return;
    }
    if (t == null) {
        onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
        return;
    }
    if (get() != 0) {
        actual.onNext(t);
        BackpressureHelper.produced(this, 1);
    } else {
        onOverflow();
    }
}

LATEST(LatestAsyncEmitter<T>)

策略:Flowable的异步缓存池满了,直接丢掉后续发送的数据。但会保证最后一条数据强制缓存,并保证发送,通过内置final AtomicReference<T> queue; 实现。也就是说无论如何,Subscriber都会接收到Flowable发送的最后一条数据。

代码比较复杂,分析略!

BUFFER(BufferAsyncEmitter<T>)

策略:通过内置

final SpscLinkedArrayQueue<T> queue;

缓存所有超限数据,该策略不会抛出MissingBackpressureException异常,不会丢失数据(在内置缓存大小范围内),但会导致OOM。

SpscLinkedArrayQueue内部实现为数组,大小默认128,当缓存数超过128,SpscLinkedArrayQueue会重新分配缓存空间,简而言之,这是一个无限大的缓存池。

static final class BufferAsyncEmitter<T> extends BaseEmitter<T> {

    // ......
    final SpscLinkedArrayQueue<T> queue;
    BufferAsyncEmitter(Subscriber<? super T> actual, int capacityHint) {
        super(actual);
        this.queue = new SpscLinkedArrayQueue<T>(capacityHint);
        this.wip = new AtomicInteger();
    }
    @Override
    public void onNext(T t) {
        // ......
        queue.offer(t);
        // ......
    }
    // ......
}

3.3.响应式拉取(Flowable特有)

Subscription接口定义了两个方法:

void cancel();  取消Subscriber与Flowable的订阅关系。

void request(long n);  声明并控制订阅者对Publisher数据的需求量。

Flowable在设计的时候,采用了一种新的思路——响应式拉取方式,来设置订阅者对数据的请求数量,Flowable可以根据订阅者的需求量,按需发送数据。

如果不显式调用Subscription.request(Long)则默认订阅者的需求量为零,所以如果Subscriber不显示调用request方法,Flowable发送的数据并不会交给Subscriber处理。

Subscription.request(Long)可多次调用,Subscriber总共获取的数据量是多次调用累计之和(不大于Flowable发出的数据量),如果Subscriber请求的数据量小于Flowable发送的数据量,那么需求之外的数据仍然放到了异步缓存池中。如果异步缓存池超限,会导致MissingBackpressureException异常。

Subscription.request(0)时,报错:java.lang.IllegalArgumentException: n > 0 required but it was 0

3.4.FlowableEmitter(数据发送器)

响应式拉取的初衷,是为了设置订阅者对数据的请求数量,Flowable可以根据订阅者的需求量,按需发送数据。

FlowableEmitter可以实现这一需求,FlowableEmitter内置long requested();可以获取当前Flowable未完成的请求数量。随着Flowable不停发送数据,long requested();值会相应变化。

public interface FlowableEmitter<T> extends Emitter<T> {

    void setDisposable(@Nullable Disposable s);
    void setCancellable(@Nullable Cancellable c);
    long requested();
    boolean isCancelled();
    @NonNull
    FlowableEmitter<T> serialize();
    @Experimental
    boolean tryOnError(@NonNull Throwable t);
}

重点关注long requested();方法!

同步状态下:

FlowableEmitter.requested();方法获取到的最大未完成请求数 == Subscriber 中Subscription.request(Long)中请求的数量,与Flowable缓存队列大小无关。

FlowableEmitter.requested();的实时值 = Subscription.request(Long)中请求的数量 – Flowable已经发送的数量。

异步状态下:

FlowableEmitter.requested();方法获取到的最大未完成请求数 == 128;等于Flowable缓存队列大小,与Subscriber 中Subscription.request(Long)中请求多少无关。

Flowable有一个异步缓存池,Flowable发送的数据,先放到异步缓存池中,再由异步缓存池交给Subscriber。所以Flowable在发送数据时,首先需要考虑的不是Subscriber的数据请求量,而是缓存池中能不能放得下,放得下直接塞入,放不下执行背压策略。至于是否超出了Subscriber的数据需求量,可以在缓存池向其传递数据时,再作判断,如果未超出,则将缓存池中的数据传递,如果超出了,则不传递。

FlowableEmitter.requested();的实时值 = Flowable异步缓存池当前可用空间大小。并不是下游真正的数据请求数量!

随着缓存池把数据发送给Subscriber,Flowable将执行清理缓存的策略,异步缓存池中的数据并不是向Subscriber发送一条便清理一条,而是每等累积到N条时,清理一次。通过e.requested()获取到的值,正是在异步缓存池清理数据时回升的。也就是异步缓存池每次清理后,都会导致Flowable未完成请求数量的回升,理想状态下,缓存池在Flowable发送数据时一直有空间,这样既不会引发背压异常,也不会导致数据遗失。

例如:

1.Flowable有N条数据(N ≤ 128),Subscription.request(M)(M < N)

Flowable实际发送N条数据到异步缓存池,再由异步缓存池发送M条数据给Subscriber。

2.Flowable有N条数据(N ≤ 128),Subscription.request(M)(M ≥ N)

Flowable实际发送N条数据到异步缓存池,再由异步缓存池发送N条数据给Subscriber。

3.Flowable有N条数据(N > 128),Subscription.request(M)(M ≤ 128)

Flowable实际发送K(N ≥ K ≥ 128,K的具体数量和Subscriber接收处理数据的速度有关)条数据到异步缓存池,其余部分执行背压策略,再由异步缓存池发送M条数据给Subscriber。

4.Flowable有N条数据(N > 128),Subscription.request(M)(M > 128)

Flowable实际发送K(N ≥ K ≥ 128,K的具体数量和Subscriber接收处理数据的速度有关)条数据到异步缓存池,其余部分执行背压策略,再由异步缓存池发送M(M < K) / K(M ≥ K)条数据给Subscriber。

注意:onComplete(),onError()并不减少requested值。

3.5.背压问题解决方案:(Flowable按需发送数据)

如果由于订阅者处理数据缓慢,导致Flowable异步缓存池被塞满且无法得到清理,通过requested()获取的值也就变成了0,会根据BackpressureStrategy背压策略的不同,抛出MissingBackpressureException异常,或做额外缓存或者丢掉这条数据。所以Flowable只需要在e.requested()等于0时,暂停发送数据,便可解决背压问题!!!

完整方案:

发送方按需发送;

@Override

public void subscribe(FlowableEmitter<Integer> e) throws Exception {

    int i = 0;
    while (true) {
        if (e.requested() == 0) continue; //此处添加代码,让flowable按需发送数据
        System.out.println("发送---->" + i);
        i++;
        e.onNext(i);
    }
}

接收方处理一条再请求下一条;

private Subscription mSubscription;

@Override
public void onSubscribe(Subscription s) {
    s.request(1);            // 设置初始请求数据量为1
    mSubscription = s;
}
@Override
public void onNext(Integer integer) {
    try {
        Thread.sleep(50);
        System.out.println("接收------>" + integer);
        mSubscription.request(1);// 每接收到一条数据增加一条请求量
    } catch (InterruptedException ignore) {
    }
}

4.Observable(数据源类二)

Observable抽象类Observable是接口ObservableSource的一个抽象实现,我们可以通过Observable创建一个可观察对象发送数据流。

// 发送对应的方法

Observable.create(new ObservableOnSubscribe<String>() {
    // 默认在主线程里执行该方法
    @Override
    public void subscribe(@NonNull ObservableEmitter<String> e) throws Exception {
        e.onNext("Hello");
        e.onNext("World");
        // 结束标识
        e.onComplete();
    }
});

ObservableEmitter:数据发送器

public interface Emitter<T> {

    void onNext(@NonNull T value);
    void onError(@NonNull Throwable error);
    void onComplete();
}

(1)onNext:用来发送数据,可多次调用,每调用一次发送一条数据

(2)onError:用来发送异常通知,只发送一次,若多次调用只发送第一条

(3)onComplete:用来发送完成通知,只发送一次,若多次调用只发送第一条

onError与onComplete互斥,两个方法只能调用一个不能同时调用!

接口Observer中的三个方法(onNext,onError,onComplete)正好与Emitter中的三个方法相对应,分别对Emitter中对应方法的行为作出响应。

Emitter调用onNext发送数据时,Observer会通过onNext接收数据。

Emitter调用onError发送异常通知时,Observer会通过onError接收异常通知。

Emitter调用onComplete发送完成通知时,Observer会通过onComplete接收完成通知。

Observable 在2.0中被设计成了无处理背压能力的,避免了1.0中出现的Observable不能合理背压,导致无法预料的MissingBackpressureException出现。

参数对象ObservableOnSubscribe会被存储在返回的 Observable 对象中,它的作用相当于一个计划表,当 Observable 被订阅的时候,ObservableOnSubscribe的 subscribe () 方法会自动被调用,事件序列就会依照设定依次触发(ObservableEmitter将会被调用2次 onNext() 和1次 onComplete())。这样,由被观察者调用了观察者的回调方法,就实现了由被观察者向观察者的事件传递,即观察者模式。

4.1.Observable

通过Observable.create、Observable.interval等创建型操作符生成的Observable。

(1)当一个订阅者订阅Observable(包括重复订阅)时,Observable会重新开始发送数据给订阅者。

(2)当多个订阅者订阅到同一个Observable时,订阅者收到的数据是相互独立的。

(3)(2)情况下,当其中一个订阅者取消订阅,Observable只会停止对该订阅者的数据发送。

4.2.ConnectableObservable

ConnectableObservable通过Observable.publish();Observable.reply(int N) 转换生成。publish():订阅者订阅ConnectableObservable只能收到订阅行为以后数据源发送的数据;replay(int N):订阅者订阅ConnectableObservable能收到订阅行为之前发送的N个数据。

(1)无论ConnectableObservable有没有订阅者,只要调用了ConnectableObservable的connect方法,原始Observable就开始发送数据。

(2)connect返回一个Disposable对象,调用了该对象的dispose方法,原始Observable将会停止发送数据,所有ConnectableObservable的订阅者也无法收到数据。

(3)在调用connect返回的Disposable对象后,如果重新调用了connect方法,那么原始Observable会重新发送数据。

(4)当多个订阅者订阅同一个ConnectableObservable时,它们收到的数据是相同的。

(5)当一个订阅者取消对ConnectableObservable,不会影响其他订阅者收到消息。

(6)多个订阅者订阅ConnectableObservable,真正原始属于源并不知道订阅者的存在,和订阅者交互的实际是ConnectableObservable,起到一个承上启下的中介作用。

4.3.refObservable

refObservable指的是由ConnectableObservable通过.refCount() 转换而来的Observable;或者由Observable.share()方法转换成的Observable。

(1)当第一个订阅者订阅refObservable后,原始Observable开始发送数据。

(2)之后的订阅者订阅到refObservable后,只能接收到订阅之后,原始Observable 发送的数据。

(3)(2)情况下,如果一个订阅者取消订阅refObservable,不影响其他订阅者接收数据;如果全部订阅者都取消了订阅,则原始Observable停止发送数据。

4.4.autoObservable

autoObservable由ConnectableObservable通过.autoConnect(int N)转换而来的特殊Observable:

(1)当有N个订阅者订阅autoObservable后,原始Observable开始发送数据。

(2)只要原始Observable开始发送数据,即使所有的订阅者都取消了对autoObservable的订阅,原始Observable也不会停止发送数据。

5.Observer and Subscriber

// 创建被观察者

Observable.just("Hello", "World")
        // 将被观察者切换到子线程
        .subscribeOn(Schedulers.io())
        // 将观察者切换到主线程
        .observeOn(AndroidSchedulers.mainThread())
        // 创建观察者并订阅
        .subscribe(new Observer<String>() {
            // Disposable 相当于RxJava1.x中的 Subscription,用于解除订阅
            private Disposable disposable;
            @Override
            public void onSubscribe(Disposable d) {
                disposable = d;
            }
            @Override
            public void onNext(String s) {
                Log.i("JAVA", "被观察者向观察者发送的数据:" + s);
                if (s == "-1") {   // "-1" 时为异常数据,解除订阅
                    disposable.dispose();
                }
            }
            @Override
            public void onError(Throwable e) {}
            @Override
            public void onComplete() {}
        });

订阅做了以下几件件事:

(1)首先调用onSubscribe()方法

(2)其次调用ObservableOnSubscribe的subscribe方法,事件发送的逻辑由这里开始运行,之后针对性地调用Observer相应方法进行数据响应。

另外,直接订阅或传入消费者会产生一个新的类:Disposable,用于取消订阅。Observable在订阅取消之后发送的数据,Observer将不会接收。

简化订阅:上面Observer可以通过Consumer(Consumer就是消费者的意思,替代1.0的Action0,Action1),来消费onSubscribe,onNext,onComplete,onError等事件:

RxJava 3中没有了一系列的Action/Func接口,取而代之的是与Java8命名类似的函数式接口。Action类似于RxJava1.x中的Action0,区别在于Action允许抛出异常;Consumer即消费者,用于接收单个值,BiConsumer则是接收两个值,Function用于变换对象Function的泛型第一个为接收参数的数据类型,第二个为转换后要发送的数据类型Predicate用于判断。这些接口命名大多参照了Java8。

Observable.just("Hello", "World")

        .subscribe(new Consumer<String>() {
            @Override
            public void accept(@NonNull String s) throws Exception {

                                       //这里接收数据(onNext)。
                Log.i("JAVA", "被观察者向观察者发送的数据:" + s);
            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(@NonNull Throwable throwable) throws Exception {

                                       //这里接收异常Throwable。
            }
        }, new Action() {
            @Override
            public void run() throws Exception {

                                       //这里接收onComplete。
            }
        }, new Consumer<Disposable>() {
            @Override
            public void accept(@NonNull Disposable disposable) throws Exception {

                                       //这里相当于onSubscribe。
            }
        });

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章