14

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?

Ralph Bergmann
  • 3,015
  • 4
  • 30
  • 61

4 Answers4

11

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.

rafakob
  • 3,946
  • 3
  • 26
  • 36
1

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

E-Kami
  • 2,529
  • 5
  • 30
  • 50
0

You can use onErrorResumeNext or onExceptionResumeNext and pass there "error" value. You can look for other error handlings depends on your needs here

Community
  • 1
  • 1
Adam Radomski
  • 2,515
  • 2
  • 17
  • 28
  • I'm not sure if this works. The pic http://reactivex.io/documentation/operators/images/onErrorResumeNext.png looks that it also calls `onComplete` after `onError` – Ralph Bergmann May 11 '16 at 10:48
  • @RalphBergmann it should work here's the exapmles: http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/#maskingexceptions – Adam Radomski May 11 '16 at 10:56
-3

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));
David Hackro
  • 3,652
  • 6
  • 41
  • 61