El artículo discutirá
RxJava2 en
RxJava2 versión
2.0.6 y posterior. Si alguien chocó y no puede resolverlo, o no escuchó sobre este problema en absoluto, le pido un gato. Impulsaron la traducción de problemas en la
production después de la transición de
RxJava1 a
RxJava2 . El original fue escrito el 28 de diciembre de 2017, pero es mejor averiguarlo tarde que nunca.
Todos somos buenos desarrolladores y
onError errores en
onError cuando usamos
RxJava . Esto significa que nos hemos protegido de los bloqueos de aplicaciones, ¿verdad?
No, no es verdadA continuación, veremos un par de ejemplos en los que la aplicación se
RxJava debido a
RxJava , incluso si
onError implementa correctamente.
RxJava errores básicos en RxJava
RxJava usa
RxJavaPlugins.onError como el controlador de errores base. Maneja todos los errores que no se pueden entregar al suscriptor. Por defecto, todos los errores se envían, por lo que pueden producirse bloqueos críticos de la aplicación.
2.0.6 describen este comportamiento:
Uno de los objetivos del diseño 2.x es la falta de errores perdidos. A veces, una secuencia termina o se cancela antes de que la fuente onError . En este caso, el error no tiene a dónde ir y se envía a RxJavaPlugins.onError
Si RxJava no tiene un controlador de errores básico, dichos errores se ocultarán a nosotros y los desarrolladores no estarán al tanto de posibles problemas en el código.
A partir de la versión
2.0.6 ,
RxJavaPlugins.onError intenta ser más inteligente y comparte errores de biblioteca / implementación y situaciones en las que no se puede entregar un error. Los errores asignados a la categoría de "errores" se llaman como están, mientras que el resto se envuelven en
UndeliverableException y luego se llaman. Puede ver toda esta lógica
aquí (
isBug onError e
isBug ).
Uno de los principales errores de los novatos en el
RxJava OnErrorNotImplementedException es
OnErrorNotImplementedException . Este error ocurre si
observable causa un error y el método
onError no está implementado en el suscriptor. Este error es un ejemplo de un error que para el controlador de errores básico
RxJava es un "error" y no se convierte en una
UndeliverableException no
UndeliverableException .
Excepción imposible de entregar
Dado que los errores relacionados con los "errores" son fáciles de corregir, no nos detendremos en ellos. Los errores que
RxJava envuelve en una
RxJava son más interesantes, ya que no siempre es obvio por qué el error no se puede entregar a
onError .
Los casos en que esto puede suceder dependen de lo que las fuentes y los suscriptores hagan específicamente. Consideraremos los ejemplos a continuación, pero en general podemos decir que tal error ocurre si no hay un suscriptor activo a quien se le pueda entregar el error.
Ejemplo con zipWith ()
La primera opción en la que puede generar una
zipWith es la
zipWith .
val observable1 = Observable.error<Int>(Exception()) val observable2 = Observable.error<Int>(Exception()) val zipper = BiFunction<Int, Int, String> { one, two -> "$one - $two" } observable1.zipWith(observable2, zipper) .subscribe( { System.out.println(it) }, { it.printStackTrace() } )
Combinamos dos fuentes juntas, cada una de las cuales causa un error. Que esperamos Podemos suponer que
onError se llamará dos veces, pero esto contradice
la especificación de Reactive streams .
Después de una sola llamada a un evento de terminal ( onError , onCompelete ), se requiere que no se realicen más llamadas
Resulta que con una sola llamada a
onError segunda llamada ya no es posible. ¿Qué sucede cuando se produce un segundo error en la fuente? Se entregará a
RxJavaPlugins.onError .
Una manera fácil de entrar en esta situación es usar
zip para combinar llamadas de red (por ejemplo, dos llamadas de
Retrofit devuelven
Observable ). Si se produce un error en ambas llamadas (por ejemplo, no hay conexión a Internet), ambas fuentes causarán errores, el primero de los cuales caerá en la implementación
onError y el segundo se entregará al controlador de errores base (
RxJavaPlugins.onError ).
Ejemplo de ConnectableObservable sin suscriptores
ConnectableObservable también puede generar una
UndeliverableException . Vale la pena recordar que
ConnectableObservable genera eventos independientemente de la presencia de suscriptores activos, solo llame al método
connect() . Si se produce un error en
ConnectableObservable cuando no hay suscriptores, se entregará al controlador de errores base.
Aquí hay un ejemplo bastante inocente que podría causar tal error:
someApi.retrofitCall()
Si
someApi.retrofitCall() causa un error (por ejemplo, no hay conexión a Internet), la aplicación se bloqueará porque el error de red se entregará al
RxJava errores base
RxJava .
Este ejemplo parece ficticio, pero es muy fácil entrar en una situación en la que
ConnectableObservable todavía está conectado, pero no tiene suscriptores. Me encontré con esto al usar
autoConnect() al llamar a una API.
autoConnect() no deshabilita automáticamente
Observable . Me
onStop en el método de
Activity onStop , pero el resultado de la llamada de red regresó después de la destrucción de la
Activity y la aplicación se
UndeliverableException con
UndeliverableException .
Manejo de errores
Entonces, ¿qué hacer con estos errores?
El primer paso es observar los errores que ocurren e intentar determinar qué los causa. Idealmente, si puede solucionar el problema en su origen para evitar que el error se
RxJavaPlugins.onError a
RxJavaPlugins.onError .
La solución para el ejemplo
zipWith es tomar una o ambas fuentes e implementar
uno de los métodos para detectar errores en ellas. Por ejemplo, puede usar
onErrorReturn para pasar el valor predeterminado en lugar de un error.
El ejemplo de
ConnectableObservable es más sencillo de solucionar, solo asegúrese de desconectar
Observable cuando el último suscriptor se da de baja.
autoConnect() , por ejemplo, tiene una implementación sobrecargada que toma una función que captura el tiempo de conexión (
se puede ver más aquí ).
Otra forma de resolver el problema es reemplazar el controlador de errores base por el suyo. El
RxJavaPlugins.setErrorHandler(Consumer<Throwable>) lo ayudará con esto. Si esta es la solución adecuada para usted, puede detectar todos los errores enviados a
RxJavaPlugins.onError y manejarlos como mejor le parezca. Esta solución puede ser bastante complicada: recuerde que
RxJavaPlugins.onError recibe errores de todas
RxJava transmisiones de
RxJava en la aplicación.
Si crea manualmente su
Observable , puede llamar a
emitter.tryOnError() lugar de
emitter.tryOnError() . Este método informa un error solo si la transmisión no finaliza y tiene suscriptores. Recuerda que este método es experimental.
La moraleja de este artículo es que no puede estar seguro de que no haya errores al trabajar con RxJava si simplemente implementó
onError en los suscriptores. Debe tener en cuenta las situaciones en las que los errores pueden no estar disponibles para los suscriptores y asegurarse de que se manejan estas situaciones.