Ralph Bergmann
Ralph Bergmann

Reputation: 3076

RxJava + Retrofit + polling

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

Answers (4)

David Hackro
David Hackro

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

rafakob
rafakob

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

E-Kami
E-Kami

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

Adam Radomski
Adam Radomski

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

Related Questions