Rxjava operator Repeatwhen () and Retrywhen ()

Source: Internet
Author: User
Tags throwable

The first time I saw the. repeatwhen () and. Retrywhen () The two operators were very confused. It must be said that they are definitely a powerful contender for the "most puzzling marbles".

However, they are all very useful operators: allow you to re-subscribe to the observable that have ended conditionally. I've recently studied how they work, and now I want to try to explain them (because I've also spent some energy trying to fathom them).

Comparison of repeat and retry

First, find out what the most intuitive difference is between. Repeat () and. Retry (). The problem is not difficult: the difference is in what kind of termination events trigger a re-subscription.

A re-subscription is triggered when. Repeat () receives the. oncompleted () event.

A re-subscription is triggered when. Retry () receives the. OnError () event.

However, this simple narrative is not yet satisfactory. What if you want to implement a re-subscription that delays several seconds? Or do you want to see the error to decide if you should re-subscribe? In this case, the. Repeatwhen () and. Retrywhen () are involved, as they allow you to provide custom logic for the retry.

Notification Handler

You can use a function called Notificationhandler to implement the retry logic. This is the. Retrywhen () method signature (Translator Note: Method signature, which refers to the method name, parameter type, number of parameters, etc.):

retryWhen(Func1<? super Observable<? extends java.lang.Throwable>,? extends Observable<?>> notificationHandler)

The signature is long and cannot even be read in a breath. The reason I find it hard to understand is because there are a whole bunch of generic conventions.

After simplification, it consists of three parts:

    1. Func1 is like a factory class, used to implement your own retry logic.
    2. The input is a observable< throwable>.
    3. The output is a observable<?>.

First, let's take a look at the last part. The event to be sent by the returned observable<?> determines whether the re-subscription will occur. If the oncompleted or OnError event is sent, the re-subscription will not be triggered. In contrast, if it sends a OnNext event, it triggers a re-subscription (regardless of what onnext actually is). This is why wildcard characters are used as generic types: This is just a notification (next, error, or completed), a very important notice.

Source each time a call is OnError (Throwable), observable is passed as input into the method. In other words, every time it's called you need to decide if you need to re-subscribe.

When the subscription occurs, the factory Func1 is called, which prepares the retry logic. In that case, when OnError is called, the retry logic you have defined will be able to handle it.

Here's an example of where we should subscribe to source, for example, to request a throwable only if it is ioexception, otherwise it is not (re-subscribed).

Source.retrywhen (New FUNC1<Observable< extends Throwable>, Observable<? >>() {@Override public Observable <? > Call (Observable<? extends Throwable> errors) { return errors.flatmap (new Func1<throwable, Observable<? >> () {@Override public Observable <? > Call (throwable error) { //for ioexceptions, we retryif (Error instanceof                IOException) { return observable.just (null);              } //For anything else, don ' t retryreturn observable.error (error);          }            }); }          })                                

Since every error has been flatmap, we cannot trigger a re-subscription by directly calling. OnNext (null) or. OnError (error) to avoid a re-subscription.

Experience

Here are some important points about. Repeatwhen () and. Retrywhen () that we should keep in mind.

    • . Repeatwhen () is very similar to. retrywhen (), except that it no longer responds to onerror as a retry condition, but rather oncompleted. Because OnCompleted does not have a type, all inputs become observable.

    • The subscription Notificationhandler (that is, FUNC1) for each event stream is called only once. This also makes sense, because you have an observable observable that can send any number of error.

    • The input observable must be the source of the output observable. You have to react to observable and then send events based on it; you can't just return a generic generic stream.

In other words, you can't do similar things:

 .retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {              @Override public Observable<?> call(Observable<? extends Throwable> errors) {                return Observable.just(null);}            })

Not only does it not work, but it also interrupts your chain structure. What you should do, and at least do, is to return the input as a result, like this:

.retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {              @Override public Observable<?> call(Observable<? extends Throwable> errors) {                return errors;              }            })

(Incidentally, this is logically the same as using only the. Retry () operator)

    • The input observable only fires when the terminating event occurs (for. Repeatwhen () is oncompleted, and for. Retrywhen () is onerror). It does not receive any OnNext notifications from the source, so you cannot decide to re-subscribe by observing the events being sent. If you really need to do this, you should add an operator like. Takeuntil () to intercept the event stream.
How to use

Now, let's say you have a general understanding of the. Repeatwhen () and. Retrywhen (), what kind of thin logic can you put into Notificationhandler?

Use. Repeatwhen () +. Delay () to poll data periodically:

source.repeatWhen(new Func1<Observable<? extends Void>, Observable<?>>() {              @Override public Observable<?> call(Observable<? extends Void> completed) {                return completed.delay(5, TimeUnit.SECONDS);              }            })

OnNext () will not be re-subscribed to source until Notificationhandler is sent. Deferred re-subscriptions are gracefully implemented, thus avoiding uninterrupted data polling, because delay is delayed for a period of time before the OnNext () is sent.

Alternatively , use the. FlatMap () +. Timer () to implement deferred re-subscription:
(Note: In Rxjava 1.0.0 and beyond, the official is no longer advocating the use of the. Timer () operator because. Interval () has the same functionality)

Source.retrywhen (New FUNC1<Observable< extends Throwable>, Observable<? >>() {@Override public Observable <? > Call (Observable<? extends Throwable> errors) { return errors.flatmap (new< /c10> func1<throwable, Observable<? >> () {@Override public Observable <?                  > Call (throwable error) { return Observable.timer (5, timeunit.seconds);              }                }); }            })

This alternative is useful when you need to collaborate with other logic, such as ...

Use. FLATMAP () +. Timer () to achieve a limited number of re-subscriptions

source.retrywhen (new  Func1 <observable<? Extends Throwable>, observable<?>> () { @Override  public  observable<?> call  (observable<? extends Throwable> errors) {return  errors.zipwith (Observable.range (1 , 3 ), new  func2< Throwable, Integer, integer> () { @Override  public  integer call  (throwable throwable, integer i) {return  i;              }                }); }            })

The final result is that each error is paired with an output in range, like this:

zip(error1, 1) -> onNext(1)  <-- Resubscribe  zip(error2, 2) -> onNext(2)  <-- Resubscribe  zip(error3, 3) -> onNext(3)  <-- Resubscribe  onCompleted()                <-- No resubscription

Because when the fourth error occurs, the number in range (1,3) is exhausted, so it implicitly calls OnCompleted (), which results in the end of the entire zip. Further retries are prevented.

Combining variable delay policies with the retry mechanism of the number limit

Source.retrywhen (Newfunc1<observable<? Extends Throwable>, observable<?>> () {@Override  PublicObservable<?>Pager(observable< extends throwable> errors) {returnErrors.zipwith (Observable.range (1,3),NewFunc2<throwable, Integer, integer> () {@Override  PublicIntegerPager(Throwable throwable, Integer i) {returnI }}). FlatMap (NewFunc1<integer, observable<? Extends Long>> () {@Override  Publicobservable<? Extends long>Pager(Integer RetryCount) {returnObservable.timer ((Long) Math.pow (5, RetryCount), timeunit.seconds);              }                }); }            })

In comparison with this use case, I think the combination of. FlatMap () +.timer () is preferable to using only. Delay () because we can modify the delay time by the number of retries. Retry three times, and each retry time is 5 ^ retrycount, just through the combination of some operators to help us achieve the exponential Backoff algorithm (translator Note: can refer to binary exponential backoff algorithm).

Wen/Xiao Tang (author of Jane's book)
Original link: http://www.jianshu.com/p/023a5f60e6d0

Rxjava operator Repeatwhen () and Retrywhen ()

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.