I have the following Observable which will execute a REST-Call with Retrofit each 30 seconds:
Subscription subscription = Observable.interval(0, REFRESH_INTERVAL, TimeUnit.SECONDS)
.concatMap(new Func1<Long, Observable<Response>>() {
@Override
public Observable<Response> call(Long time) {
return webservice.callRetrofitServiceWithRx(parameter);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new UpdateSuccessAction(), new UpdateErrorAction());
It may happen that (especially the REST-Call) will throw an exception (e.g. no internet connection).
What I would like to achieve:
The Observable should emit/expose the exception, so that I can display an error message on the UI, but it should continue emitting items (retry in 30 seconds).
Currently research
If I do not define any special behaviour the Observable emits the Exception and stops working (= NO retry in 30 seconds).
If I try the retry operator, the exception will be swallowed and not exposed, so I can not display an error in the ui.
If I try the onErrorReturn operator, I can handle the exception, but no retry is possible as far as I know.
Workaround
My current workaround is to re-subscribe to this Observable, but I would like to know if someone has a more elegant solution.
Geek Tip #2: When we have a use-case where we wish to execute the work first and then postpone the emission for a specific amount of time, we may use the Delay Operator. Let’s take a look at the RxJava Interval Operator, which generates an Observable that emits a sequence of integers separated by a time interval.
Retry on Error The normal sequence may be broken by a temporary system failure or backend error. In these situations, we want to retry and wait until the sequence is fixed. Luckily, RxJava gives us options to perform exactly that. 4.1. Retry
Action on Error With doOnError, we can invoke whatever action that is needed when there is an error: In case of an exception being thrown while performing the action, RxJava wraps the exception in a CompositeException: 3.2. Resume With Default Items
RxJava also provides a fallback method that allows continuing the sequence with a provided Observable when an exception (but no error) is raised: As the code above shows, when an error does occur, the onExceptionResumeNext won't kick in to resume the sequence.
I'm assuming doOnError will fit your need (for logging the error), combined with retry, e.g:
Subscription subscription = Observable.interval(0, REFRESH_INTERVAL, TimeUnit.SECONDS)
.concatMap(new Func1<Long, Observable<Response>>() {
@Override
public Observable<Response> call(Long time) {
return webservice.callRetrofitServiceWithRx(parameter);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(new UpdateErrorAction())
.retry()
.subscribe(new UpdateSuccessAction());
With the help of the other answer if found a solution.
At first I defined a RetryWithDelay-Function which starts the retry after 30 seconds and not immediately.
private static class RetryWithDelay
implements Func1<Observable<? extends Throwable>, Observable<?>> {
@Override
public Observable<?> call(Observable<? extends Throwable> attempts) {
return attempts.flatMap(new Func1<Throwable, Observable<?>>() {
@Override
public Observable<?> call(Throwable throwable) {
return Observable.timer(CallBO.REFRESH_INTERVAL_IN_SEC, }
});
}
}
Which I then used in this Observable-Chain:
Subscription subscription = Observable.interval(0, REFRESH_INTERVAL, TimeUnit.SECONDS)
.concatMap(new Func1<Long, Observable<Response>>() {
@Override
public Observable<Response> call(Long time) {
return webservice.callRetrofitServiceWithRx(parameter);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(new UpdateErrorAction())
.retryWhen(new RetryWithDelay())
.subscribe(new UpdateSuccessAction());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With