Reputation: 3076
I have a Retrofit call and want to recall it every 30sec. To do that I use an Observable.interval(0, 30, TimeUnit.SECONDS)
Observable
.interval(0, 30, TimeUnit.SECONDS)
.flatMap(x -> RestApi.instance().getUsers())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
// ...
},
error -> Timber.e(error, "can't load users"));
My problem: If the api call fails, onError
is called and the subscription unsubscribes and the polling isn't working anymore :-(
To catch the api error I added a retryWhen
Observable
.interval(0, 30, TimeUnit.SECONDS)
.flatMap(x -> RestApi.instance().getUsers()
.retryWhen(errors -> errors
.flatMap(error -> Observable.timer(15, TimeUnit.SECONDS))))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
// ...
},
error -> Timber.e(error, "can't load users"));
This catches the error but I get multiple api calls over the time. Every 30sec I get a new poll signal which ends in a new api request. But if the api request fails it retries itself. So I have a new request plus all retries.
My question: How can I handle an api error without unsubscribing from the poll signal?
Upvotes: 14
Views: 6813
Reputation: 3712
You can use this code, in this code implement the numbers attempts and time delay between request
private static int COUNTER_START = 1;
private static final int ATTEMPTS = 6;
private static final int ORIGINAL_DELAY_IN_SECONDS = 2;
remoteData.getAllRides(idSearch)
.repeatWhen(new Func1<Observable<? extends Void>, Observable<?>>() {
@Override
public Observable<?> call(Observable<? extends Void> observable) {
return observable.flatMap(new Func1<Void, Observable<?>>() {
@Override
public Observable<?> call(Void aVoid) {
if(COUNTER_START > ATTEMPTS){
throw new RuntimeException();
}
COUNTER_START++;
return Observable.timer(ORIGINAL_DELAY_IN_SECONDS, TimeUnit.SECONDS);
}
});
}
})
.takeUntil(new Func1<RideResponse, Boolean>() {
@Override
public Boolean call(RideResponse rideResponse) {
return rideResponse.getState().equals("finished");//this is the validation finish polling
}
}).filter(new Func1<RideResponse, Boolean>() {
@Override
public Boolean call(RideResponse rideResponse) {
return rideResponse.getState().equals("finished"); //this is the validation finish polling
}
}).map(rideResponse -> Log.e("",rideResponse.toString()))
.doOnError(err -> Log.e("Polling", "Error retrieving messages: " + err));
Upvotes: -3
Reputation: 4346
Read how to properly use retryWhen
and repeatWhen
.
http://blog.danlew.net/2016/01/25/rxjavas-repeatwhen-and-retrywhen-explained/
And how to use onError
operators:
http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/
It's really easy w Rx :) I'm not gonna give you a final solution, just play around with it and try to understand the flow here.
Upvotes: 11
Reputation: 2669
If you want your getUsers
request to not end up in onError
when a request fails, instead of returning Observable<yourUserType> getUsers()
, make it return an Observable<Response<yourUserType>> getUsers()
. This way you'll be able to intercept the network error in the Response
object.
This method works only if you are using retrofit 2.x
Upvotes: 1
Reputation: 2575
You can use onErrorResumeNext or onExceptionResumeNext and pass there "error" value. You can look for other error handlings depends on your needs here
Upvotes: 0