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.