L'article abordera
UndeliverableException
dans
RxJava2
version
2.0.6
et
2.0.6
ultérieures. Si quelqu'un est entré en collision et ne peut pas le comprendre, ou n'a pas entendu parler de ce problème du tout - je demande un chat. Ils ont provoqué la traduction de problèmes de
production
après la transition de
RxJava1
à
RxJava2
. L'original a été écrit le 28 décembre 2017, mais il vaut mieux le découvrir tard que jamais.
Nous sommes tous de bons développeurs et nous
RxJava
erreurs dans
onError
lorsque nous utilisons
RxJava
. Cela signifie que nous nous sommes protégés contre les plantages d'applications, non?
Non, pas vrai.Ci-dessous, nous examinerons quelques exemples dans lesquels l'application se
RxJava
raison de
RxJava
, même si
onError
correctement implémenté.
RxJava
erreurs de base dans RxJava
RxJava
utilise
RxJavaPlugins.onError
comme gestionnaire d'erreur de base. Il gère toutes les erreurs qui ne peuvent pas être transmises à l'abonné. Par défaut, toutes les erreurs lui sont envoyées, ce qui peut entraîner des plantages critiques des applications.
2.0.6
décrivent ce comportement:
L'un des objectifs de la conception 2.x est l'absence d'erreurs perdues. Parfois, une séquence se termine ou est annulée avant que la source ne déclenche onError
. Dans ce cas, l'erreur n'a nulle part où aller et elle est envoyée à RxJavaPlugins.onError
Si RxJava n'a pas de gestionnaire d'erreurs de base, ces erreurs nous seront cachées et les développeurs seront dans l'ignorance des problèmes potentiels dans le code.
À partir de la version
2.0.6
,
RxJavaPlugins.onError
essaie d'être plus intelligent et partage les erreurs de bibliothèque / d'implémentation et les situations où une erreur ne peut pas être transmise. Les erreurs affectées à la catégorie des «bogues» sont appelées telles quelles, tandis que les autres sont encapsulées dans
UndeliverableException
puis appelées. Vous pouvez voir toute cette logique
ici (
isBug
onError
et
isBug
).
L'une des principales erreurs des débutants dans la
RxJava
OnErrorNotImplementedException
est
OnErrorNotImplementedException
. Cette erreur se produit si
observable
provoque une erreur et que la méthode
onError
n'est pas implémentée dans l'abonné. Cette erreur est un exemple d'erreur qui pour le gestionnaire d'erreurs de base
RxJava
est un «bogue» et ne se transforme pas en une
UndeliverableException
.
UndeliverableException
Comme les erreurs liées aux «bugs» sont faciles à corriger, nous ne nous attarderons pas dessus. Les erreurs que
RxJava
dans une
UndeliverableException
sont plus intéressantes, car il n'est pas toujours évident de savoir pourquoi l'erreur ne peut pas être transmise à
onError
.
Les cas dans lesquels cela peut se produire dépendent de ce que les sources et les abonnés font spécifiquement. Nous considérerons les exemples ci-dessous, mais en général, nous pouvons dire qu'une telle erreur se produit s'il n'y a aucun abonné actif à qui l'erreur peut être transmise.
Exemple avec zipWith ()
La première option dans laquelle vous pouvez
zipWith
une
UndeliverableException
est l'
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() } )
Nous combinons deux sources ensemble, chacune provoquant une erreur. Qu'attendons-nous? Nous pouvons supposer que
onError
sera appelé deux fois, mais cela contredit
la spécification des Reactive streams
.
Après un seul appel à un événement terminal ( onError
, onCompelete
), il est nécessaire de ne plus effectuer d'appels
Il s'avère qu'avec un seul appel à
onError
deuxième appel n'est plus possible. Que se passe-t-il lorsqu'une deuxième erreur se produit dans la source? Il sera livré à
RxJavaPlugins.onError
.
Un moyen simple de se retrouver dans cette situation consiste à utiliser
zip
pour combiner les appels réseau (par exemple, deux appels
Retrofit
renvoyant
Observable
). Si une erreur se produit dans les deux appels (par exemple, il n'y a pas de connexion Internet), les deux sources provoqueront des erreurs, dont la première tombera dans l'implémentation
onError
et la seconde sera remise au gestionnaire d'erreurs de base (
RxJavaPlugins.onError
).
Exemple ConnectableObservable sans abonnés
ConnectableObservable
peut également
UndeliverableException
une
UndeliverableException
. Il convient de rappeler que
ConnectableObservable
déclenche des événements indépendamment de la présence d'abonnés actifs, il suffit d'appeler la méthode
connect()
. Si une erreur se produit dans
ConnectableObservable
alors qu'il n'y a pas d'abonnés, elle sera transmise au gestionnaire d'erreurs de base.
Voici un exemple assez innocent qui pourrait provoquer une telle erreur:
someApi.retrofitCall()
Si
someApi.retrofitCall()
provoque une erreur (par exemple, il n'y a pas de connexion Internet), l'application se bloque car l'erreur réseau sera transmise au
RxJava
erreurs de base
RxJava
.
Cet exemple semble fictif, mais il est très facile de se retrouver dans une situation où
ConnectableObservable
est toujours connecté, mais il n'a pas d'abonnés. Je suis tombé sur cela lors de l'utilisation de
autoConnect()
lors de l'appel d'une API.
autoConnect()
ne désactive pas automatiquement
Observable
. Je me suis désabonné de la méthode d'
Activity
onStop
, mais le résultat de l'appel réseau est revenu après la destruction de l'
Activity
et l'application
UndeliverableException
avec
UndeliverableException
.
Gestion des erreurs
Que faire de ces erreurs?
La première étape consiste à examiner les erreurs qui se produisent et à essayer de déterminer leurs causes. Idéalement, si vous pouvez résoudre le problème à sa source pour éviter que l'erreur ne soit
RxJavaPlugins.onError
à
RxJavaPlugins.onError
.
La solution pour l'exemple
zipWith
est de prendre une ou les deux sources et d'implémenter l'
une des méthodes pour y détecter les erreurs. Par exemple, vous pouvez utiliser
onErrorReturn
pour transmettre la valeur par défaut au lieu d'une erreur.
L'exemple
ConnectableObservable
est plus simple à corriger - assurez-vous simplement de déconnecter
Observable
lorsque le dernier abonné se désabonne.
autoConnect()
, par exemple, a une implémentation surchargée qui prend une fonction qui attrape le temps de connexion (
plus peut être vu ici ).
Une autre façon de résoudre le problème consiste à remplacer le gestionnaire d'erreurs de base par le vôtre. La
RxJavaPlugins.setErrorHandler(Consumer<Throwable>)
vous y aidera. Si c'est la solution qui vous convient, vous pouvez intercepter toutes les erreurs envoyées à
RxJavaPlugins.onError
et les traiter comme bon vous semble. Cette solution peut être assez compliquée - n'oubliez pas que
RxJavaPlugins.onError
reçoit des erreurs de tous les flux
RxJava
de l'application.
Si vous créez manuellement votre
Observable
, vous pouvez appeler
emitter.tryOnError()
au lieu de
emitter.tryOnError()
. Cette méthode signale une erreur uniquement si le flux n'est pas terminé et a des abonnés. N'oubliez pas que cette méthode est expérimentale.
La morale de cet article est que vous ne pouvez pas être sûr qu'il n'y a aucune erreur lorsque vous travaillez avec RxJava si vous avez simplement implémenté
onError
dans les abonnés. Vous devez être conscient des situations dans lesquelles les erreurs peuvent ne pas être disponibles pour les abonnés et assurez-vous que ces situations sont gérées.