处理 Kotlin Coroutines 中自定义 okhttp 拦截器抛出的异常
Handle exceptions thrown by a custom okhttp Interceptor in Kotlin Coroutines
我在我的 Android 应用程序中使用自定义 Interceptor
和 Retrofit 客户端,在某些特定情况下会抛出异常。我正在尝试使用 Kotlin 协程使其工作。
问题是我无法处理前面提到的错误,因为在拦截器实例中抛出异常的那一刻,它崩溃了整个应用程序而不是被协程捕获 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)
我应该怎么做才能正确地从拦截器中捕获和处理这个异常?我错过了什么吗?
我不知道你到底需要什么,但是这样理解:
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
okhttp3.Response response = chain.proceed(request);
// todo deal with the issues the way you need to
if (response.code() == SomeCode) {
//do something
return response;
}
return response;
}
})
.build();
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(url)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
您应该继承 IOException
并使用它来将信息从您的拦截器发送到您的调用代码。
我们将 IllegalStateException
等其他异常视为应用程序崩溃,不会将它们发送到线程边界,因为我们不想让大多数调用者因捕获它们而增加负担。
您可能会在您的自定义 Interceptor
和 return 中捕获到一个包含特定 message
和 code
的空响应。我已经实现了一个自定义 Interceptor
来处理诸如当您没有互联网连接或互联网连接速度较慢等情况时...实际上协程的挂起函数在处理网络调用时会抛出异常。根据我的经验,您可以采用两种方法。 1. 将所有网络调用包装在 try...catch
或 2. 创建一个自定义 Interceptor
并在那里处理异常和 return一些具体的回应。
方法一:
try {
webservice.login(username, password)
} catch (e: Exception) {
//...
}
方法二:
创建自定义 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()
}
}
}
我已经为 LoggingInterceptor
的完整实现创建了要点,其中包含请求和响应的打印日志。 LoggingInterceptor
我在我的 Android 应用程序中使用自定义 Interceptor
和 Retrofit 客户端,在某些特定情况下会抛出异常。我正在尝试使用 Kotlin 协程使其工作。
问题是我无法处理前面提到的错误,因为在拦截器实例中抛出异常的那一刻,它崩溃了整个应用程序而不是被协程捕获 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)
我应该怎么做才能正确地从拦截器中捕获和处理这个异常?我错过了什么吗?
我不知道你到底需要什么,但是这样理解:
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
okhttp3.Response response = chain.proceed(request);
// todo deal with the issues the way you need to
if (response.code() == SomeCode) {
//do something
return response;
}
return response;
}
})
.build();
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(url)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
您应该继承 IOException
并使用它来将信息从您的拦截器发送到您的调用代码。
我们将 IllegalStateException
等其他异常视为应用程序崩溃,不会将它们发送到线程边界,因为我们不想让大多数调用者因捕获它们而增加负担。
您可能会在您的自定义 Interceptor
和 return 中捕获到一个包含特定 message
和 code
的空响应。我已经实现了一个自定义 Interceptor
来处理诸如当您没有互联网连接或互联网连接速度较慢等情况时...实际上协程的挂起函数在处理网络调用时会抛出异常。根据我的经验,您可以采用两种方法。 1. 将所有网络调用包装在 try...catch
或 2. 创建一个自定义 Interceptor
并在那里处理异常和 return一些具体的回应。
方法一:
try {
webservice.login(username, password)
} catch (e: Exception) {
//...
}
方法二:
创建自定义 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()
}
}
}
我已经为 LoggingInterceptor
的完整实现创建了要点,其中包含请求和响应的打印日志。 LoggingInterceptor