我正在尝试针对按钮点击的某些操作进行错误处理。对于绑定我使用RxAndroid + RxAndroid。似乎它必须使用下面的代码,但它不与onBackpressure()
的注释行一起使用:
CurrentUser signIn() {
throw new RuntimeException();
}
Integer x = 1;
PublishSubject<Throwable> loginingFailedSubject = PublishSubject.create();
@Override
public void onStart() {
super.onStart();
RxView.clicks(loginButton)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext((v) -> setLoginingWaiting())
.observeOn(Schedulers.newThread())
.map((v) -> signIn())
.lift(new SuppressErrorOperator<>(throwable -> {
Log.e("MyTag", "Oops, failed " + x.toString() + " times!");
++x;
loginingFailedSubject.onNext(throwable);
}))
//.onBackpressureBuffer()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(user -> setLoginedUser(user));
loginingFailedSubject
.observeOn(AndroidSchedulers.mainThread())
.subscribe(throwable -> setLoginingFailed(throwable));
}
这是SuppressErrorOperator
代码:
public final class SuppressErrorOperator<T> implements
Observable.Operator<T, T> {
final Action1<Throwable> errorHandler;
public SuppressErrorOperator(Action1<Throwable> errorHandler) {
this.errorHandler = errorHandler;
}
public SuppressErrorOperator() {this(null);}
@Override
public Subscriber<? super T> call(final Subscriber<? super T> subscriber) {
return new Subscriber<T>(subscriber) {
@Override
public void onCompleted() {
subscriber.onCompleted();
}
@Override
public void onError(Throwable e) {
if (errorHandler != null) {
errorHandler.call(e);
}
}
@Override
public void onNext(T t) {
subscriber.onNext(t);
}
};
}
}
这是我在最后100次点击后在logcat中得到的内容:
Oops, failed 16 times!
它在exaclty 16次之后停止,并且在17日,它运行setLoginingWaiting()
(我看到它,因为此方法禁用按钮也意味着,每个请求没有人可以点击超过1次。或者接近该数字)就这样。好像它根本没有到达.lift()
。
但如果我取消注释.onBackpressureBuffer()
,它现在完全有效!我读了很多关于背压的文章。我甚至花了一整天时间来理解Observable
,Subscriber
e.t.c的源代码。
我知道,16 - 是Android缓冲区的常量大小。但为什么会受到打击?我不经常点击按钮。此外,根本没有onNext()
,所以缓冲区在任何情况下都不能超过! onError()
吞噬了所有Operator
。
我也知道observeOn()
通过pull协议工作,所以它内部想要使用request()
。如果我在observeOn()
之前发表评论.subscribe(user -> setLoginedUser(user));
- 它也会起作用(但当然,这是不可接受的)。
但是为什么以及为什么需要onBackpressure()
才能活着?另外,为什么它会在没有MissingBackpressureException
之类的任何例外情况下死亡?
答案 0 :(得分:3)
问题在于您干扰了流的生命周期。 map
崩溃,但您取消了异常,并且没有值发送到下游。如果下游没有得到任何值,它就不知道它应该请求更多,因此整个序列停止,让缓冲区填满。 onBackpressureBuffer
仅适用于请求Long.MAX_VALUE并保持源运行。
但请注意,map
不应该像现在这样工作,而是取消订阅该函数的第一个错误信号。
正确的选择是:
RxView.clicks(loginButton)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext((v) -> setLoginingWaiting())
.observeOn(Schedulers.newThread())
.flatMap(v -> {
try {
return Observable.just(signIn());
} catch (Throwable ex) {
Log.e("MyTag", "Oops, failed " + x.toString() + " times!");
++x;
loginingFailedSubject.onNext(ex);
return Observable.empty();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(user -> setLoginedUser(user));
答案 1 :(得分:1)
OperatorObserveOn有一个大小为RxRingBuffer.SIZE的队列(在android上为16),如果超过此队列的大小,将抛出MissingBackpressure异常。
通常,为了避免背压问题,您可以限制切换线程,限制事件发射(油门,缓冲等)或使用onBackpressureXXX操作符。
虽然看起来像你的情况 - 登录按钮 - 你只需要一次处理一个请求,所以为什么不用ProgressBar隐藏按钮或者在请求时设置启用(false)
答案 2 :(得分:1)
我认为OperatorObserveOn::ObserveOnSubscriber
中存在错误。
在init方法中,它设置新的生产者并由内部订阅者(子)requested
递增ObserveOnSubscriber
(requested
)。但是当ObserveOnSubscriber
是孩子时,它的默认值(如果是Android - 16)为Subscriber::requested
且requested
的实际值为ObserveOnSubscriber::requested
。
问题是,Subscriber::requested
已使用且未更新。所以解决方法是重写ObserveOnSubscriber
的init方法(我刚刚复制了OperatorObserveOn的源代码,并在包中使用与RxJava同名的副本)。
void init() {
// don't want this code in the constructor because `this` can escape through the
// setProducer call
Subscriber<? super T> localChild = child;
localChild.setProducer(new Producer() {
@Override
public void request(long n) {
if (n > 0L) {
BackpressureUtils.getAndAddRequest(requested, n);
ObserveOnSubscriber.this.request(requested.get());
schedule();
}
}
});
localChild.add(recursiveScheduler);
localChild.add(this);
}
我还在github上创建了issue。