You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Retrofit 2:超时失败重试时如何通过编程方式动态增加超时时间

Dynamic Timeout Retry for Retrofit Requests

I get it—static timeout setups just don’t cut it when you need to bump the timeout only for retries after a timeout failure, then revert back to the original values. Let’s walk through two solid, practical approaches to solve this:

Approach 1: Manual Retry in onFailure() Callback

This is straightforward for one-off requests where you want explicit control over the retry flow.

First, save your original timeout values when setting up your OkHttpClient:

// Define your original timeout values (adjust these to your needs)
private val originalConnectTimeout = 30L // Seconds
private val originalReadTimeout = 30L
private val originalWriteTimeout = 30L

// Initialize your base OkHttpClient with original timeouts
private var okHttpClient = OkHttpClient.Builder()
    .connectTimeout(originalConnectTimeout, TimeUnit.SECONDS)
    .readTimeout(originalReadTimeout, TimeUnit.SECONDS)
    .writeTimeout(originalWriteTimeout, TimeUnit.SECONDS)
    .build()

Then, in your onFailure() callback, check if the failure is a timeout, create a temporary client with extended timeouts, retry the request, and revert back to the original client afterward:

yourRetrofitCall.enqueue(object : Callback<YourResponseModel> {
    override fun onResponse(call: Call<YourResponseModel>, response: Response<YourResponseModel>) {
        // Handle successful response as usual
    }

    override fun onFailure(call: Call<YourResponseModel>, t: Throwable) {
        if (t is SocketTimeoutException) {
            // Calculate extended timeouts (add 20s to original values)
            val extendedConnectTimeout = originalConnectTimeout + 20
            val extendedReadTimeout = originalReadTimeout + 20
            val extendedWriteTimeout = originalWriteTimeout + 20

            // Create a temporary OkHttpClient with extended timeouts
            val tempClient = okHttpClient.newBuilder()
                .connectTimeout(extendedConnectTimeout, TimeUnit.SECONDS)
                .readTimeout(extendedReadTimeout, TimeUnit.SECONDS)
                .writeTimeout(extendedWriteTimeout, TimeUnit.SECONDS)
                .build()

            // Retry the request with the temporary client
            tempClient.newCall(call.request()).enqueue(object : Callback<YourResponseModel> {
                override fun onResponse(call: Call<YourResponseModel>, response: Response<YourResponseModel>) {
                    // Retry succeeded! Revert to original OkHttpClient
                    resetToOriginalTimeouts()
                    // Handle the successful retry response
                }

                override fun onFailure(call: Call<YourResponseModel>, t: Throwable) {
                    // Retry failed, still revert to original timeouts
                    resetToOriginalTimeouts()
                    // Handle retry failure
                }
            })
        } else {
            // Handle non-timeout failures normally
        }
    }
})

// Helper method to reset OkHttpClient to original timeouts
private fun resetToOriginalTimeouts() {
    okHttpClient = okHttpClient.newBuilder()
        .connectTimeout(originalConnectTimeout, TimeUnit.SECONDS)
        .readTimeout(originalReadTimeout, TimeUnit.SECONDS)
        .writeTimeout(originalWriteTimeout, TimeUnit.SECONDS)
        .build()
}

Approach 2: Custom Interceptor for Reusable Retry Logic

If you want this behavior across multiple requests, a custom OkHttp Interceptor is cleaner and avoids code duplication.

Create an interceptor that handles retries with dynamic timeouts:

class DynamicTimeoutRetryInterceptor(
    private val originalConnectTimeout: Long,
    private val originalReadTimeout: Long,
    private val originalWriteTimeout: Long,
    private val maxRetries: Int = 1 // Adjust max retry count as needed
) : Interceptor {

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

        while (retryAttempt <= maxRetries) {
            try {
                // Use original timeouts for first attempt, extended for retries
                val currentChain = if (retryAttempt > 0) {
                    val timeoutExtension = 20 * retryAttempt // 20s per retry
                    chain.withConnectTimeout(originalConnectTimeout + timeoutExtension, TimeUnit.SECONDS)
                        .withReadTimeout(originalReadTimeout + timeoutExtension, TimeUnit.SECONDS)
                        .withWriteTimeout(originalWriteTimeout + timeoutExtension, TimeUnit.SECONDS)
                } else {
                    chain
                }

                val response = currentChain.proceed(request)
                if (response.isSuccessful) {
                    return response
                }
            } catch (e: SocketTimeoutException) {
                retryAttempt++
                if (retryAttempt > maxRetries) {
                    throw e // No more retries left, propagate the exception
                }
            }
        }

        throw IOException("Request failed after $maxRetries retries")
    }
}

Add this interceptor to your OkHttpClient setup:

private val okHttpClient = OkHttpClient.Builder()
    .connectTimeout(originalConnectTimeout, TimeUnit.SECONDS)
    .readTimeout(originalReadTimeout, TimeUnit.SECONDS)
    .writeTimeout(originalWriteTimeout, TimeUnit.SECONDS)
    .addInterceptor(DynamicTimeoutRetryInterceptor(
        originalConnectTimeout,
        originalReadTimeout,
        originalWriteTimeout
    ))
    .build()

Key Notes

  • Thread Safety: If you’re modifying the global OkHttpClient (like in Approach 1), make sure to handle thread safety if multiple requests are running concurrently. Alternatively, avoid modifying the global client and always create a new instance for retries.
  • Retry Limits: Don’t forget to set a max retry count to avoid infinite loops if the endpoint is consistently timing out.
  • Error Handling: Both approaches let you distinguish between timeout failures and other errors, so you only apply the extended timeout when it’s needed.

内容的提问来源于stack exchange,提问作者Prachi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 23:24:04