Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

November Discount Launch 2022 – Top
We’re finally running a Black Friday launch. All Courses are 30% off until end-of-day today:

>> GET ACCESS NOW

November Discount Launch 2022 – TEMP TOP (NPI)
We’re finally running a Black Friday launch. All Courses are 30% off until end-of-day today:

>> GET ACCESS NOW

1. Overview

In this tutorial, we'll see how to use retry with delay in RxJava. When working with Observable, it is quite likely to get an error instead of a success. In such cases, we can use retry with delay to reattempt after some time.

2. Project Setup

To begin this, let's create a Maven or a Gradle project. Here, we're using a Maven-based project. Let's add the rxjava dependency to our pom.xml file:

<dependency>
    <groupId>io.reactivex.rxjava2</groupId>
    <artifactId>rxjava</artifactId>
    <version>2.2.21</version>
</dependency>

The dependency can be found on the Maven Central Page.

3. Retry in the Observable Lifecycle

Retry is used when an Observable source emits an error. This is a resubscription to the source in the hopes of getting completed without errors in the next attempt. Therefore, retry() and its other variants come into the picture only when the Observable emits an error.

3.1. Overloaded Variants of Retry

Apart from the retry() method discussed in the above section, RxJava provides two overloaded variants, retry(long) and retry(Func2).

The retry(long) returns an Observable that mirrors the source Observable. We resubscribe to this mirror only as long as it calls onError the specified number of times.

The retry(Func2) method takes a function that accepts a Throwable as input and produces a boolean as output. Here, we resubscribe to the mirror only if it returns true for the specific Exception and the retry count.

3.2. RetryWhen vs Retry

The retryWhen(Func1) provides us with an option to include custom logic to determine whether or not we should resubscribe to the mirror of the original Observable. This method accepts a function that takes the exception thrown by the onError handler.

If the function emits an item, we resubscribe to the mirror, while we don't if it produces an onError notification.

4. Code Examples

Let's now watch these concepts in action with the help of a few code snippets.

4.1. Successful Observable

If an Observable call succeeds, then the Observer's onNext method is invoked. Let's see an example:
@Test
public void givenObservable_whenSuccess_thenOnNext(){
    Observable.just(remoteCallSuccess())
        .subscribe(success -> {
            System.out.println("Success");
            System.out.println(success);
        }, err -> {
            System.out.println("Error");
            System.out.println(err);
        });
}

The method remoteCallSuccess() completes successfully and returns a String. In this case, the onNext() method of the subscriber is invoked, printing success messages.

4.2. Erroneous Observable

Let's introduce a new utility function called remoteCallError() that mocks calling a remote server. This method is vulnerable to errors. Therefore, our retryWhen logic would kick in:
@Test
public void givenObservable_whenError_thenOnError(){
    Observable.just(remoteCallError())
        .subscribe(success -> {
            System.out.println("Success");
            System.out.println(success);
        }, err -> {
            System.out.println("Error");
            System.out.println(err);
        });
}

In this case, the remoteCallError() method causes an error. Therefore, the onError() method of the subscriber gets called. Consequently, error messages get printed.

4.3. Retrying with Delay

We can add a delay during the resubscription process:
@Test
public void givenError_whenRetry_thenCanDelay(){
    Observable.just(remoteCallError())
        .retryWhen(attempts -> {
            return attempts.flatMap(err -> {
                if (customChecker(err)) {
                    return Observable.timer(5000, TimeUnit.MILLISECONDS);
                } else {
                    return Observable.error(err);
                }
            });
        });
}

Similar to the previous case, the remoteCallError() method causes an error. Here, however, instead of notifying the observer's onError() method, we give this Observable a chance to resubcribe. For this, we pass the exception to a customChecker() method, which returns a boolean. If the response is true, we return a new Observable with a delay of 5000 milliseconds (5 seconds). Otherwise, we return an error and notify the observer.

5. Conclusion

In this tutorial, we saw how to use retryWhen and some of its related functions. We understood the cases under which retry and retryWhen help us solve our issues. The source code is available over on GitHub.
November Discount Launch 2022 – Bottom
We’re finally running a Black Friday launch. All Courses are 30% off until end-of-day today:

>> GET ACCESS NOW

Generic footer banner
guest
1 Comment
Oldest
Newest
Inline Feedbacks
View all comments