2

In an Android app scenario, I want to fetch some Observable<Data> from network, and there are multiple Observer<Data> subscribed to it to update corresponding views. In case of error -say a timeout- show a button to the user to try again.

How can I do the try again part? can I tell the observable to re-execute its logic again without re-subscribing to it?

y.allam
  • 1,446
  • 13
  • 24

2 Answers2

2

Let's assume you have two buttons, "Retry" and "Cancel", initially hidden. Create two Observables retryButtonClicks and cancelButtonClicks. Then, apply the retryWhen operator to the designated download flow and act upon the signals of these button clicks:

download.retryWhen(errors -> {
    return errors
        .observeOn(AndroidSchedulers.mainThread())
        .flatMap(e -> {
            // show the "Retry" and "Cancel" buttons around here
            return Observable.amb(
                    retryButtonClicks.take(1).map(v -> "Retry"),
                    cancelButtonClicks.take(1).map(v -> "Cancel")
                )
                .doOnNext(v -> { /* hide the "Retry" and "Cancel" buttons */ });
    })
    .takeWhile(v -> "Retry".equals(v))
    ;
});
akarnokd
  • 69,132
  • 14
  • 157
  • 192
  • Can you please explain what's happening here? and how to create the buttons observables? – y.allam Jan 29 '18 at 12:00
  • There is a stream of errors with `retryWhen`. If an error occurs, another flow is mapped in which takes the button click signal from either buttons unless cancel was clicked. Once the "Retry" button was clicked, the `retryWhen` will resubscribe to the source. Please look at the RxBinding library for creating button click `Observable`s. – akarnokd Jan 29 '18 at 12:44
  • Thanks a lot for the answer and explanation – y.allam Feb 03 '18 at 11:18
  • Another question, using `retryWhen` prevents the error from being propagated to the observer's `onError` callback. How to solve this while using `retryWhen`? – y.allam Feb 04 '18 at 13:32
  • What do you mean? – akarnokd Feb 04 '18 at 13:40
  • Assume observable: `Observable ob = Observable.create(e -> { e.onNext(1); e.onNext(2); e.onError(new Throwable("Intentional error")); }).retryWhen(....)` with the retry logic you provided added to this observable. And the observer: `ob.subscribe( o -> Log.d(TAG, o.toString()), e -> Log.d(TAG, e.getMessage()) );` The "Intentional error" string doesn't get logged – y.allam Feb 04 '18 at 14:00
  • In the `flatMap` inside `retryWhen`, check the `e` and return `Observable.error(e)` to stop the retry. – akarnokd Feb 04 '18 at 15:15
  • But i need to keep both, return the error to be consumed by the Observer, and then execute the retry when retry button clicked! – y.allam Feb 05 '18 at 16:06
  • That is not possible. The moment the error reaches the Observer, the stream is over. – akarnokd Feb 05 '18 at 17:09
1

There is actually specific methods

retry()

Returns an Observable that mirrors the source Observable, resubscribing to it if it calls onError (infinite retry count).

and retry(long count)

Returns an Observable that mirrors the source Observable, resubscribing to it if it calls onError up to a specified number of retries.

Read more in an article and in the docs

Rainmaker
  • 10,294
  • 9
  • 54
  • 89
  • But these will retry directly after onError called, what I want to do is to retry when the button is clicked – y.allam Jan 27 '18 at 18:38
  • https://stackoverflow.com/questions/36809467/rxandroid-retry-observable-on-click This worked for me but I used it for rxjs. – AliF50 Feb 20 '18 at 19:31