estoy usando una costumbre Interceptor
junto con el cliente Retrofit en mi aplicación de Android, arroja una excepción en algunas circunstancias específicas. Estoy tratando de hacer que funcione usando corrutinas de Kotlin.
El problema es que no puedo manejar el error mencionado anteriormente, ya que en el momento en que se lanza la excepción desde la instancia de Interceptor, bloquea toda la aplicación en lugar de quedar atrapada en la corrutina. try/catch
declaración. Mientras estaba usando la implementación de Rx, la excepción se propagó sin problemas al onError
devolución de llamada donde pude manejarlo de la manera que lo necesitaba.
Supongo que esto está relacionado de alguna manera con los subprocesos subyacentes que se utilizan para la llamada de red, consulte los registros a continuación desde el lugar donde se realiza la llamada, desde el interceptor justo antes de lanzar la excepción y el seguimiento de la pila:
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)
¿Qué se supone que debo hacer para poder capturar y manejar esta excepción del Interceptor correctamente? ¿Me estoy perdiendo de algo?
Deberías subclasificar IOException
y utilícelo para enviar información desde sus interceptores a su código de llamada.
Consideramos otras excepciones como IllegalStateException
para que la aplicación se bloquee y no los envíe más allá de los límites del subproceso porque no queremos agobiar a la mayoría de las personas que llaman con la captura de ellos.
-
Esta parece ser la solución más limpia del problema. Solo me pregunto por qué en la implementación de Rx, cualquiera de las excepciones se reenvió a la persona que llama de todos modos. ¿Hay algo interno dentro del adaptador Rx de Retrofit?
– mrpasqal
5 de noviembre de 2019 a las 12:44
-
TBH Creo que es una decisión de diseño realmente mala. ¿Por qué cree que podría decidir por el usuario si quiere o no ‘asumir la carga’ de detectar una excepción más genérica? ¿Por qué mis excepciones personalizadas se ven obligadas a ser IOException incluso si semánticamente no lo son?
– Fiódor Volchiok
19 de diciembre de 2019 a las 11:25
-
@fyodor aquí hay una discusión sobre esta decisión de diseño: github.com/square/okhttp/pull/5457
–Jesse Wilson
19 de diciembre de 2019 a las 17:08
-
@JesseWilson desde cuando? ¡Esto me hizo perder medio día! ¿Por qué? No tiene ningún sentido para mí. IOException = problema de red. Utilizo esa excepción para decidir cuándo reintentar automáticamente cuando la red vuelve a estar en línea. he creado un problema al respecto github.com/square/retrofit/issues/3505
– Daniele Segato
22 de diciembre de 2020 a las 17:56
Puede detectar la excepción en su costumbre Interceptor
y devolver una respuesta vacía con algunos específicos message
y code
. He implementado una costumbre Interceptor
para manejar la situación, como cuando no tiene una conexión a Internet lenta, etc. En realidad, las funciones de suspensión de coroutine arrojan una excepción cuando se trata de llamadas de red. En mi experiencia, puedes seguir 2 enfoques. 1. envuelva su llamada de toda la red try...catch
o 2. crear una costumbre Interceptor
y manejar excepciones allí y devolver alguna respuesta específica.
Enfoque 1:
try {
webservice.login(username, password)
} catch (e: Exception) {
//...
}
Enfoque 2:
crear una costumbre Interceptor
y manejar la excepción allí.
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()
}
}
}
He creado Gist para la implementación completa de LoggingInterceptor
con registros de impresión de solicitud y respuesta. Interceptor de registro
-
Gracias por tu
Approach 2
. ¡Es tan útil!– Teo
19 de febrero de 2021 a las 1:28
-
Oh hombre, el mejor enfoque en todo Internet.
– Yasin Hajilou
9 de enero de 2022 a las 8:40
-
¡Oh hombre! salvaste mi océano de tiempo, simplemente no hay palabras para tu solución. El enfoque 2 está funcionando como el infierno. muchas gracias
–Eddie Brock
26 de abril de 2022 a las 13:17
No sé qué es exactamente lo que necesita, pero se entiende así:
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();
“Supongo que esto está relacionado de alguna manera con los subprocesos subyacentes que se utilizan para la llamada de red”. ¿Está diciendo que en la implementación de Rx, tanto lo que tiene como “Subproceso en ejecución” como “Subproceso interceptor” eran iguales? Es posible que desee editar su pregunta y proporcionar un ejemplo reproducible mínimo, que muestre el código del interceptor y el seguimiento completo de la pila de su excepción personalizada no controlada.
– CommonsWare
5 de noviembre de 2019 a las 0:33
Solo
IOException
se propaga a la persona que llama. Todas las demás excepciones se delegan al controlador de excepciones no detectadas. Esto significa que en lugar de lanzar unIllegalStateException
deberías lanzar unIOException
en cambio.– Egor Neliuba
5 de noviembre de 2019 a las 7:48