Retry Operator in Kotlin Flow
Tags: kotlin, android
Published: 2022-10-21 12:25:23

---
title: Retry Operator in Kotlin Flow
published: true
description: In this blog, we will learn about the Retry Operator in Kotlin Flow.
tags: kotlin, android
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9dq6dwnk1faxb65uyk67.png
canonical_url: https://amitshekhar.me/blog/retry-operator-in-kotlin-flow
---

I am [**Amit Shekhar**](https://amitshekhar.me), a mentor helping developers in getting high-paying tech jobs.

In this blog, we will learn about the **Retry** Operator in Kotlin Flow.

**This article was originally published at [amitshekhar.me](https://amitshekhar.me/blog/retry-operator-in-kotlin-flow).**

I will be using the following project for the implementation part. The project follows a basic MVVM Architecture for simplicity. You can find the complete code for the implementation mentioned in this blog in the project itself.

GitHub Project: [Learn Kotlin Flow](https://github.com/amitshekhariitbhu/Learn-Kotlin-Flow)

When we talk about retrying a task using operators in Kotlin Flow, we talk about the following two operators:

- `retryWhen`
- `retry`

Both operators can be used interchangeably in most cases, we will learn about them today.

## retryWhen

Let's look at the source code to understand the definition of the `retryWhen` operator.

```kotlin
fun <T> Flow<T>.retryWhen(predicate: suspend FlowCollector<T>.(cause: Throwable, attempt: Long) -> Boolean): Flow<T>
```

And, we use this operator as below:

```kotlin
.retryWhen { cause, attempt ->

}
```

Here, we have two parameters as follows:

- **cause**: This `cause` is `Throwable` which is the base class for all errors and exceptions.
- **attempt**: This `attempt` is the number that represents the current attempt. It starts with **zero**.

For example, if there is an exception when we started the task, we will receive the cause(exception) and attempt(0).

The `retryWhen` takes a `predicate` function to decide whether to retry or not.

If the `predicate` function returns `true`, then only it will retry else it will not.

For example, we can do as below:

```kotlin
.retryWhen { cause, attempt ->
    if (cause is IOException && attempt < 3) {
        delay(2000)
        return@retryWhen true
    } else {
        return@retryWhen false
    }
}
```

In this case, we are returning `true` when the cause is `IOException`, and the `attempt` count is less than `3`.

So, it will only retry if the condition is satisfied.

**Note**: As the `predicate` function is suspending function, we can call another suspending function from it.

If we notice in the above code, we have called the `delay(2000)`, so that it retries only after a delay of `2` seconds.

# retry

This is the definition of the `retry` Flow operator.

```kotlin
fun <T> Flow<T>.retry(
    retries: Long = Long.MAX_VALUE,
    predicate: suspend (cause: Throwable) -> Boolean = { true }
): Flow<T>
```

The complete block from the source code of Kotlin Flow.

```kotlin
fun <T> Flow<T>.retry(
    retries: Long = Long.MAX_VALUE,
    predicate: suspend (cause: Throwable) -> Boolean = { true }
): Flow<T> {
    require(retries > 0) { "Expected positive amount of retries, but had $retries" }
    return retryWhen { cause, attempt -> attempt < retries && predicate(cause) }
}
```

If we see the `retry` function, it actually calls the `retryWhen` internally.

`retry` function has default arguments.

- If we do not pass the `retries`, it will use the `Long.MAX_VALUE`.
- If we do not pass the `predicate`, it will provide true.

For example, we can do as below:

```kotlin
.retry()
```

It will keep retrying until the task gets completed successfully.

For example, we can also do as below:

```kotlin
.retry(3)
```

It will only retry `3` times.

For example, we can also do as below:

```kotlin
.retry(retries = 3) { cause ->
    if (cause is IOException) {
        delay(2000)
        return@retry true
    } else {
        return@retry false
    }
}
```

Here, it becomes very similar to what we did using the `retryWhen` above.

Here, we are returning `true` when the `cause` is `IOException`. So, it will only retry when the `cause` is `IOException`.

If we notice in the above code, we have called the `delay(2000)`, so that it retries only after a delay of 2 seconds.

Now, let's see the code examples.

This is a function to simulate a long-running task with exceptions.

```kotlin
private fun doLongRunningTask(): Flow<Int> {
    return flow {
        // your code for doing a long running task
        // Added delay, random number, and exception to simulate
        delay(2000)
        val randomNumber = (0..2).random()
        if (randomNumber == 0) {
            throw IOException()
        } else if (randomNumber == 1) {
            throw IndexOutOfBoundsException()
        }
        delay(2000)
        emit(0)
    }
}
```

Now, when using the `retry` operator

```kotlin
viewModelScope.launch {
    doLongRunningTask()
        .flowOn(Dispatchers.Default)
        .retry(retries = 3) { cause ->
            if (cause is IOException) {
                delay(2000)
                return@retry true
            } else {
                return@retry false
            }
        }
        .catch {
           // error
        }
        .collect {
            // success
        }
}
```

Similarly, when using the `retryWhen` operator

```kotlin
viewModelScope.launch {
    doLongRunningTask()
        .flowOn(Dispatchers.Default)
        .retryWhen { cause, attempt ->
            if (cause is IOException && attempt < 3) {
                delay(2000)
                return@retryWhen true
            } else {
                return@retryWhen false
            }
        }
        .catch {
            // error
        }
        .collect {
            // success
        }
}
```

If we see, every time we are adding the delay of `2` seconds, but in real use-cases, we add delay with exponential backoff. Do not worry, we will implement that too.

## Retry Operator with Exponential Backoff Delay

After adding the code for the **delay with exponential backoff**

```kotlin
viewModelScope.launch {
    var currentDelay = 1000L
    val delayFactor = 2
    doLongRunningTask()
        .flowOn(Dispatchers.Default)
        .retry(retries = 3) { cause ->
            if (cause is IOException) {
                delay(currentDelay)
                currentDelay = (currentDelay * delayFactor)
                return@retry true
            } else {
                return@retry false
            }
        }
        .catch {
            // error
        }
        .collect {
            // success
        }
}
```

Here, we have created two variables:

- **currentDelay**: This represents the delay to be used in the current retry.
- **delayFactor**: We use this `delayFactor` to multiply it with the `currentDelay` to increase the delay for the next retry.

That's it, we have implemented the retry with exponential backoff delay.

You can build, run, and play all the examples in the project provided.

This way we can use `retry` and `retryWhen` Operators of Flow to solve the interesting problem in Android App Development. Remember both can be used interchangeably in most cases that we solve in Android App Development.

Master Kotlin Coroutines from here: [Mastering Kotlin Coroutines](https://amitshekhar.me/blog/kotlin-coroutines)

That's it for now.

Thanks

[**Amit Shekhar**](https://amitshekhar.me)

You can connect with me on:

- [Twitter](https://twitter.com/amitiitbhu)
- [LinkedIn](https://www.linkedin.com/in/amit-shekhar-iitbhu)
- [GitHub](https://github.com/amitshekhariitbhu)
- [Facebook](https://www.facebook.com/amit.shekhar.iitbhu)

[**Read all of my high-quality blogs here.**](https://amitshekhar.me/blog)