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.