我在手机上打开互联网后,改装和 okhttp 将无法工作

Bol*_*aul 5 kotlin retrofit okhttp

设想:

  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()
}
Run Code Online (Sandbox Code Playgroud)

服务类看起来像这样

@GET("something")
fun something(): Call<SomeResponse>
Run Code Online (Sandbox Code Playgroud)

我试过玩超时值,无论超时时间如何,我都会得到同样的错误。

为每个请求创建一个新的 http 客户端将解决这个问题,但我认为这不是一个好主意。

Vad*_*imV 6

您的问题看起来像OkHttp Bug。如果您点击该链接,您会发现有关许多可能解决方案的长时间讨论。

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

  1. 将 OkHttp 至少更新到 4.3.0。
  2. 设置ping间隔,例如1秒
okHttpClientBuilder.pingInterval(1, TimeUnit.SECONDS)
Run Code Online (Sandbox Code Playgroud)

怎么运行的

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

不推荐的解决方案,但它应该有效

  1. 关闭连接池
okHttpClientBuilder.connectionPool(new ConnectionPool(0, 1, TimeUnit.NANOSECONDS))
Run Code Online (Sandbox Code Playgroud)
  1. 使用HTTP 1.1
okHttpClientBuilder.protocols(listOf(Protocol.HTTP_1_1))
Run Code Online (Sandbox Code Playgroud)

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