在手机开启互联网后,Retrofit和okhttp无法使用

7

场景:

  1. 无网络情况下打开应用,应用将尝试发送请求,但会失败。
  2. 打开网络连接,点击重试按钮以触发网络请求。
  3. Retrofit & okhttp 一直给出 HTTP FAILED: java.net.SocketTimeoutException: timeout 错误。

重新启动应用并在启动时开启互联网连接将使一切正常工作,除非我再次关闭它并且请求失败了,从那时起,它将给我相同的错误。 我从未在Java上遇到过这个问题,只在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()
}

服务类看起来像这样

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

我尝试了不同的超时时间,但无论设置多长时间,我都会得到相同的错误。
为每个请求创建一个新的http客户端可以解决这个问题,但我认为这不是一个好主意。
1个回答

6

你遇到的问题看起来像是OkHttp Bug。如果你点击链接,你会看到许多可能的解决方案。

以下解决方案适用于我的项目:

  1. 将OkHttp更新至至少4.3.0版本。
  2. 设置ping间隔,例如1秒钟。
okHttpClientBuilder.pingInterval(1, TimeUnit.SECONDS)

工作原理

问题的根源是Android操作系统没有提供任何方法来知道连接已经失效。因此,对于库而言,连接看起来是活动的,但实际上已经死了。结果是我们在每个请求上得到超时异常。一旦我们设置了ping,OkHttp就开始发送Ping frames,这样如果服务器不响应,库就知道连接已经死了,是时候创建一个新连接了。

不推荐的解决方案,但它应该可以工作

  1. 关闭连接池
okHttpClientBuilder.connectionPool(new ConnectionPool(0, 1, TimeUnit.NANOSECONDS))
  1. 使用 Http 1.1
okHttpClientBuilder.protocols(listOf(Protocol.HTTP_1_1))

在这两种不推荐的解决方案中,您只需停止复用已打开的连接,这会使每个请求的时间略微延长。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接