Rxjava1升级Rxjava2过程中的一些知识总结

1,554 阅读2分钟

rxjava1升级rxjava2过程中的一些知识总结

背压(Backpressure)

什么是背压(Backpressure)

当被观察者(Observable)观察者(Subscriber)在不同的线程中工作,并且Observable生产事件的速度比Subscriber消费事件的速度快时,就是将事件积压在队列(queue)里面,当queue里大小超过一定大小时,就会抛出MissingBackpressureException

比如下面的代码,Observable每1毫秒生产一个事件,但Subscriber每一秒才消费一个事件,所以一运行就会立马报MissingBackpressureException错误

Observable.interval(1, TimeUnit.MILLISECONDS)
    .observeOn(Schedulers.newThread())
    .subscribe({
        Thread.sleep(1000)
        System.out.println("onNext==$it")
    })

那queue大小超过多少,会报错呢??,咱们直接看rxjava1的源码

static {
    int defaultSize = 128;

    // lower default for Android (https://github.com/ReactiveX/RxJava/issues/1820)
    if (PlatformDependent.isAndroid()) {
        defaultSize = 16;
    }

    // possible system property for overriding
    String sizeFromProperty = System.getProperty("rx.ring-buffer.size"); // also see IndexedRingBuffer
    if (sizeFromProperty != null) {
        try {
            defaultSize = Integer.parseInt(sizeFromProperty);
        } catch (NumberFormatException e) {
            System.err.println("Failed to set 'rx.buffer.size' with value " + sizeFromProperty + " => " + e.getMessage()); // NOPMD
        }
    }

    SIZE = defaultSize;
}

可见如果发现是Android的话,size是16;如果是其它的话就是128

如何改变默认的size呢?

我们先来看一下observeOn的几个重载方法

public final Observable<T> observeOn(Scheduler scheduler) {
    return observeOn(scheduler, RxRingBuffer.SIZE);
}
    
public final Observable<T> observeOn(Scheduler scheduler, int bufferSize) {
    return observeOn(scheduler, false, bufferSize);
}

可见 可以在指定消费线程的时候指定队列大小;而我们常用的是第一个方法,所以默认的大小就是上面说的16, 我们可以使用第二个重载方法指定队列大小

rxjava2的背压策略BackpressureStrategy

先看看rxjava2的默认队列大小,源码如下

public abstract class Flowable<T> implements Publisher<T> {
    /** The default buffer size. */
    static final int BUFFER_SIZE;
    static {
        BUFFER_SIZE = Math.max(1, Integer.getInteger("rx2.buffer-size", 128));
    }
    
    @CheckReturnValue
    @BackpressureSupport(BackpressureKind.FULL)
    @SchedulerSupport(SchedulerSupport.CUSTOM)
    public final Flowable<T> observeOn(Scheduler scheduler) {
        return observeOn(scheduler, false, bufferSize());
    }
    
    public static int bufferSize() {
        return BUFFER_SIZE;
    }
    
    ...
}

Observable 的源码如下:

public abstract class Observable<T> implements ObservableSource<T> {

	@CheckReturnValue
    @SchedulerSupport(SchedulerSupport.CUSTOM)
    public final Observable<T> observeOn(Scheduler scheduler) {
        return observeOn(scheduler, false, bufferSize());
    }
    
    public static int bufferSize() {
        return Flowable.bufferSize();
    }
	...
}

可见rxjava2FlowableObservable默认大小都是128(rxjava是16),但rxjava2新增的Flowable支持背压策略BackpressureStrategy, 而Observable不支持

背压策略BackpressureStrategy

  • MISSING: 缓存大小固定, 下游消费跟不上上游生产事件时, 则会报MissingBackpressureException
  • ERROR: 缓存大小固定, 下游消费跟不上上游生产事件,则会报MissingBackpressureException
  • BUFFER: 缓存无限大,上游不断生产事件,下游不断消费事件,直到程序崩溃
  • DROP: 缓存大小固定,如果下游消费跟不上上游生产事件,并且缓存已满时,如果再生产事件,则会丢掉此次生产的事件
  • LATEST: 缓存大小固定,如果下游消费跟不上上游生产事件,则会替换掉最后的事件,保持最新的事件会被消费

MISSING 和 ERROR 个人感觉没什么差别,感觉效果是一样的

顺便说一句 retrofit2使用的是BackpressureStrategy.LATEST策略

RxJava2的Single、Completable、Maybe

Single 只发射单个数据或错误事件

Single 只有 onSuccessonError 事件,onSuccess类似于ObservableFlowableonNext()方法, 用来发射生产的数据,Single只能发射一个数据,当发射多个数据时,则无效;比如

Single.create(SingleOnSubscribe<Int> {
    try {
        it.onSuccess(1)
        it.onSuccess(2)
    } catch (e: Exception) {
        it.onError(e)
    }
}).subscribe({
    System.out.println("==onNext: $it")
}, {
    System.out.println("==onError==")
})

打印结果如下:

==onNext: 1

可见并没有打印 “2” 这个数据

注意: 如果在onSuccess回调中报错,则不会走onError, 而是直接crash

Completable

Completable 只有onCompleteonError事件,它不发射任何数据给观察者,只告诉观察者完成了(onComplete)或出错了(onError)

Completable.create(CompletableOnSubscribe {
    try {
        //do something success
        it.onComplete()
    } catch (e: Exception) {
        it.onError(e)
    }
}).subscribe({
    System.out.println("==onComplete==")
}, {
    System.out.println("==onError==")
})

注意: 如果在onComplete回调中报错,则不会走onError, 而是直接crash

Maybe

Maybe 可以看成是SingleCompletable的结合,它有onSuccessonCompleteonError事件,但相互都是互斥的;同时Maybe也只能发射一个数据

如果有数据,则调用onSuccess发射数据给观察者

如果没有数据,也没有出错,则调用onComplete,告诉观察者已完成

如果出错,则调用onError,告诉观察者出错了

 Maybe.create(MaybeOnSubscribe<Int> {
    try {
        // 如果有数据
        it.onSuccess(1)
        // 如果不关心数据
        // it.onComplete() // 即使你了这个方法,也不会走onComplete回调,因为已经调了onSuccess方法
    } catch (e: Exception) {
        it.onError(e)
    }
}).subscribe({
    System.out.println("==onSuccess: $it")
}, {
    System.out.println("==onError==")
}, {
    System.out.println("==onComplete==")
})

Maybe跟Single一样,即使发射了多个数据,后面的数据也不会处理

注意: 如果在onSuccess 或 onComplete回调中报错,则不会走onError, 而是直接crash

如何处理Rxjava2不支持null的情况

在使用rxjava发射数据时,如果发射的数据时null,则会直接走onError事件

FlowableEmitter的onNext实现源码如下

@Override
public void onNext(T t) {
    ...
    if (t == null) {
        onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
        return;
    }
    ...
}

如果一定要发射null数据,但又不想走onError事件,因为它不属于异常,该怎么办呢??

发射Optional包装过的数据

1. 在使用create方法创建Flowable的时候指定Optional

Flowable.create(FlowableOnSubscribe<Optional<Int>> {
    try {
        it.onNext(Optional.ofNullable(1))
        it.onNext(Optional.ofNullable(null))
        it.onComplete()
    } catch (e: Exception) {
        it.onError(e)
    }
}, BackpressureStrategy.LATEST)
        .subscribe({
            if (it.isPresent) {
                System.out.println("==Next: ${it.get()}")
            } else {
                System.out.println("==Next: null")
            }
        }, {
            System.out.println("==onError==")
        }, {
            System.out.println("==onComplete==")
        })

2. 使用rxjava2其它操作符的时候指定Optional

比如map操作符

Flowable.just(1)
    .map {
        // 比如根据id,去找user, 可能返回null,但又不属于onError
        Optional.ofNullable(findUserById(it))
    }
    .subscribe({
        if (it.isPresent) {
            System.out.println("==Next: ${it.get()}")
        } else {
            System.out.println("==Next: null")
        }
    }, {
        System.out.println("==onError==")
    }, {
        System.out.println("==onComplete==")
    })

注意:在使用Optional的使用不要直接调get()方法,如果是null,调get()方法会报异常;而是先要用isPresent方法判断

有的时候对于观察者回调中,总是使用isPresent去判断,感觉有点不方便,由于是对于rxjava1升级到rxjava2的时候改动比较大,因为rxjava1的onNext允许返回null,这个时候我们可以封装一个BaseSubscribe

abstract class BaseSubscribe<T> : DisposableSubscriber<Optional<T>>() {

    override fun onComplete() {

    }

    override fun onNext(t: Optional<T>) {
        if (t.isPresent) {
            onOpNext(t.get())
        } else {
            onOpNext(null)
        }
    }

    abstract fun onOpNext(t: T?)

    override fun onError(t: Throwable) {

    }
}

然后调用subscribe方法的时候 全都使用BaseSubscribe类


Flowable.just(1)
	.map {
	    // 比如根据id,去找user, 可能返回null
	    Optional.ofNullable(findUserById(it))
	}
	.subscribe(object : BaseSubscribe<User>() {
	    override fun onOpNext(t: User?) {
	        System.out.println("==Next: ${t ?: null}")
	    }
	})

除了用Optional包装之外,其实自己也可以约定一个数据类型,比如Result<T>

class Result<T> {
    var data: T? = null
    ...
}