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();
}
...
}
可见rxjava2
的Flowable
、Observable
默认大小都是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
只有 onSuccess
和 onError
事件,onSuccess
类似于Observable
或Flowable
的onNext()
方法, 用来发射生产的数据,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
只有onComplete
和onError
事件,它不发射任何数据给观察者,只告诉观察者完成了(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
可以看成是Single
和Completable
的结合,它有onSuccess
、onComplete
、onError
事件,但相互都是互斥的;同时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
...
}