Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrofit & okhttp won't work after I turn on internet on phone

Tags:

Scenario:

  1. Open app without internet, the app will try to do a request, and will fail
  2. Turn on internet connection, and press retry button to trigger internet request
  3. Retrofit & okhttp will always give me HTTP FAILED: java.net.SocketTimeoutException: timeout

Restarting the app with internet enabled from start will make everything work, unless I close it again, and fail a request, from that point on it will give me the same error. I never had this issue on Java, just on Kotlin.

private val interceptor: Interceptor =
        object : Interceptor {
            override fun intercept(chain: Interceptor.Chain): Response {
                var builder = chain.request().newBuilder()
                Prefs.token?.let { token ->
                    builder = builder.addHeader("Authorization", "Bearer $token")
                }
                return chain.proceed(builder.build())
            }
        }

private val httpLoggingInterceptor: HttpLoggingInterceptor by lazy {
    val interceptor = HttpLoggingInterceptor()
    interceptor.level =
            if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
    interceptor
}
private val httpClient: OkHttpClient by lazy {
    OkHttpClient.Builder()
            .addInterceptor(httpLoggingInterceptor)
            .addInterceptor(interceptor)
            .build()
}

val retrofit: Retrofit by lazy {
    Retrofit.Builder()
            .baseUrl("https://api.secret.com/v1/")
            .addConverterFactory(GsonConverterFactory.create(gson))
            .client(httpClient)
            .build()
}

And the service classes look like this

@GET("something")
fun something(): Call<SomeResponse>

I've tried playing around with timeout values, no matter the timeout time, I will get the same error.

Creating a new http client for every request will fix the issue, but I don't think is a good idea.

like image 825
Boldijar Paul Avatar asked Aug 20 '19 07:08

Boldijar Paul


1 Answers

Your issue looks like OkHttp Bug. If you follow the link, you will find long discussion with many possible solutions.

Following solution works for my project:

  1. Update OkHttp at lest up to 4.3.0.
  2. Set ping interval, for example 1 second
okHttpClientBuilder.pingInterval(1, TimeUnit.SECONDS)

How it works

The root of the issue is that Android OS doesn't provide any way to know that connection isn't active any more. So that for library connection looks like alive, but it's already dead. As a result we get timeout exception on every request. Once we set ping, OkHttp starts sending Ping frames, so that if server doesn't respond library knows that connection is already dead, and it's time to create a new one.

Not recommended solutions, but it should work

  1. Turn-off connection pool
okHttpClientBuilder.connectionPool(new ConnectionPool(0, 1, TimeUnit.NANOSECONDS))
  1. Use Http 1.1
okHttpClientBuilder.protocols(listOf(Protocol.HTTP_1_1))

In both not recommended solutions you just stop reusing already opened connection that makes each request time little bit longer.

like image 107
VadzimV Avatar answered Nov 15 '22 03:11

VadzimV