L o a d i n g
One Interceptor to Stop Your Android App Crashing on Bad Networks
September 18, 2026

Handling Flaky Android Networks with Exponential Backoff and Jitter


Ever noticed your Android app works perfectly on Wi-Fi, but starts experiencing API failures when someone is commuting, switching networks, or using an unreliable mobile connection?


Your API might return 429 Too Many Requests or 503 Service Unavailable. Sometimes, a temporary network issue can interrupt a request at exactly the wrong moment.


One common mistake is retrying immediately.


If a server is already overloaded, sending another request right away can make the problem worse.


A better approach is exponential backoff with jitter: wait longer between retries and add a little randomness to avoid sending all retrying clients at the same time.


What Is Exponential Backoff with Jitter?

Instead of retrying immediately, the client gradually increases the waiting time after each failed attempt.

For example:

  • First retry: wait up to 500 ms
  • Second retry: wait up to 1 second
  • Third retry: wait up to 2 seconds
  • Fourth retry: wait up to 4 seconds

Jitter adds randomness to these delays, helping prevent many clients from retrying simultaneously.

Implementing Retry Logic with OkHttp

OkHttp interceptors provide a convenient place to implement shared request-handling logic for Retrofit APIs.

import okhttp3.Interceptor
import okhttp3.Response
import kotlin.math.pow
import kotlin.random.Random

class ExponentialBackoffInterceptor(
    private val maxRetries: Int = 3,
    private val baseDelayMs: Long = 500,
    private val maxDelayMs: Long = 4_000
) : Interceptor {

    override fun intercept(
        chain: Interceptor.Chain
    ): Response {
        val request = chain.request()

        // Retry only GET requests in this example.
        if (request.method != "GET") {
            return chain.proceed(request)
        }

        var attempt = 0
        var response = chain.proceed(request)

        while (
            response.code in setOf(429, 502, 503, 504) &&
            attempt < maxRetries
        ) {
            response.close()

            val exponentialDelay = (
                baseDelayMs * 2.0.pow(attempt)
            ).toLong()

            val cappedDelay = minOf(
                exponentialDelay,
                maxDelayMs
            )

            val delay = Random.nextLong(
                from = 0,
                until = cappedDelay + 1
            )

            Thread.sleep(delay)

            attempt++
            response = chain.proceed(request)
        }

        return response
    }
}

Register the interceptor with your OkHttp client:

val client = OkHttpClient.Builder()
    .addInterceptor(
        ExponentialBackoffInterceptor()
    )
.build()

Use this client with your Retrofit instance, and the retry logic will apply to requests made through that client.


Important Things to Consider

1. Don't retry every request

Retrying a GET request is generally safer than retrying a payment or order-creation request.

For operations such as POST, make sure the server supports idempotency keys or another mechanism that prevents duplicate operations before enabling retries.


2. Respect server retry instructions

When a server returns 429 or 503, it may include a Retry-After header.

A production-ready retry implementation should consider that header and enforce a maximum total retry time.


3. Avoid blocking threads for too long

Thread.sleep() does not normally block the Android UI thread when called from an asynchronous OkHttp request. However, it does occupy a worker thread while waiting.

For applications with high request volume, consider implementing retry scheduling outside the interceptor or using a retry mechanism that supports asynchronous delays.


4. Handle failures gracefully

Retries cannot guarantee that a request will succeed.

If all retry attempts fail, return the final response or propagate the network exception so your application can display an appropriate error, preserve user input, or offer a retry option.


Final Thoughts

Exponential backoff with jitter can make Android applications more resilient to temporary API failures and reduce unnecessary retry traffic.


An OkHttp interceptor can centralize this behavior, keeping networking concerns separate from your ViewModels and repositories.


However, retries should always be bounded, safe for the operation being performed, and designed to respect the server's instructions.


Reliable networking isn't about retrying every failure. It's about retrying the right failures at the right time.


- By Jenis R Amlani

WhatsApp