处理 Kotlin 协程中自定义 okhttp 拦截器抛出的异常

mrp*_*qal 18 android exception interceptor okhttp kotlin-coroutines

Interceptor在我的 Android 应用程序中使用自定义和 Retrofit 客户端,在某些特定情况下会引发异常。我正在尝试使用 Kotlin 协程使其工作。

问题是我无法处理前面提到的错误,因为在从 Interceptor 实例中抛出异常的那一刻,它会使整个应用程序崩溃,而不是在协程的try/catch语句中被捕获。当我使用 Rx 实现时,异常完美地传播到onError回调中,在那里我能够以我需要的方式处理它。

我想这与用于网络调用的底层线程有某种关系,请从调用的地方、在抛出异常之前的拦截器和堆栈跟踪中查看下面的日志:

2019-11-04 17:17:34.515 29549-29729/com.app W/TAG: Running thread: DefaultDispatcher-worker-1
2019-11-04 17:17:45.911 29549-29834/com.app W/TAG: Interceptor thread: OkHttp https://some.endpoint.com/...

2019-11-04 17:17:45.917 29549-29834/com.app E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
    Process: com.app, PID: 29549
    com.app.IllegalStateException: Passed refresh token can\'t be used for refreshing the token.
        at com.app.net.AuthInterceptor.intercept(AuthInterceptor.kt:33)
Run Code Online (Sandbox Code Playgroud)

我应该怎么做才能正确地从拦截器中捕获和处理这个异常?我错过了什么吗?

Jes*_*son 28

您应该子类化IOException并使用它来将信息从拦截器发送到您的调用代码。

我们将其他异常视为IllegalStateException应用程序崩溃,并且不会通过线程边界发送它们,因为我们不想让大多数调用者承担捕获它们的负担。

  • 说实话,我发现这是一个非常糟糕的设计决策。为什么您认为您可以为用户决定他们是否想要“承担”捕获更通用异常的负担?为什么我的自定义异常被迫为 IOException,即使在语义上它们不是? (4认同)
  • @JesseWilson 从什么时候开始?这让我浪费了半天时间!为什么?对我来说没有意义。IOException = 网络问题。我使用该异常来决定当网络恢复在线时何时自动重试。我创建了一个关于它的问题 https://github.com/square/retrofit/issues/3505 (3认同)

Ami*_*aza 10

您可以在自定义中捕获异常Interceptor并返回带有一些特定message和的空响应code。我已经实现了一个自定义Interceptor来处理诸如没有互联网连接或互联网连接缓慢等情况...实际上协程的挂起函数在处理网络调用时会抛出异常。根据我的经验,您可以遵循两种方法。1.包装您的所有网络调用try...catch2.创建一个自定义Interceptor并在那里处理异常并返回一些特定的响应。

方法一:

try {
    webservice.login(username, password)
} catch (e: Exception) {
    //...
}
Run Code Online (Sandbox Code Playgroud)

方法二:

Interceptor那里创建自定义并处理异常。

class LoggingInterceptor : Interceptor {

   @Throws(Exception::class)
   override fun intercept(chain: Interceptor.Chain): Response {
      val request = chain.request()
      try {
          val response = chain.proceed(request)
        
          val bodyString = response.body()!!.string()

          return response.newBuilder()
            .body(ResponseBody.create(response.body()?.contentType(), bodyString))
            .build()
      } catch (e: Exception) {
          e.printStackTrace()
          var msg = ""
          when (e) {
             is SocketTimeoutException -> {
                msg = "Timeout - Please check your internet connection"
             }
             is UnknownHostException -> {
                msg = "Unable to make a connection. Please check your internet"
             }
             is ConnectionShutdownException -> {
                msg = "Connection shutdown. Please check your internet"
             }
             is IOException -> {
                msg = "Server is unreachable, please try again later."
             }
             is IllegalStateException -> {
                msg = "${e.message}"
             }
             else -> {
                msg = "${e.message}"
             }
          }

          return Response.Builder()
                .request(request)
                .protocol(Protocol.HTTP_1_1)
                .code(999)
                .message(msg)
                .body(ResponseBody.create(null, "{${e}}")).build()
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

我已经创建了完整实现LoggingInterceptor请求和响应的打印日志的要点。日志拦截器

  • 感谢您的“方法 2”。这太有帮助了! (4认同)