Standard Error Handler in RxJava2 oder warum RxJava Anwendungsabstürze verursacht, selbst wenn onError implementiert ist

In diesem Artikel wird RxJava2 in RxJava2 Version 2.0.6 und RxJava2 2.0.6 . Wenn jemand zusammengestoßen ist und es nicht herausfinden kann oder überhaupt nichts von diesem Problem gehört hat, bitte ich um eine Katze. Sie veranlassten die Übersetzung von Problemen in der production nach dem Übergang von RxJava1 zu RxJava2 . Das Original wurde am 28. Dezember 2017 geschrieben, aber es ist besser, es spät herauszufinden als nie.
Wir sind alle gute Entwickler und fangen Fehler in onError wenn wir RxJava . Das heißt, wir haben uns vor Anwendungsabstürzen geschützt, oder?

Nein, nicht wahr.

Im Folgenden werden einige Beispiele aufgeführt, in denen die Anwendung aufgrund von RxJava , selbst wenn onError korrekt implementiert ist.

Grundlegende RxJava in RxJava


RxJava verwendet RxJavaPlugins.onError als RxJavaPlugins.onError . Es behandelt alle Fehler, die nicht an den Teilnehmer übermittelt werden können. Standardmäßig werden alle Fehler an das Programm gesendet, sodass kritische Anwendungsabstürze auftreten können.
2.0.6 beschreiben dieses Verhalten:
Eines der Ziele des 2.x-Designs ist das Fehlen verlorener Fehler. Manchmal endet eine Sequenz oder wird abgebrochen, bevor die Quelle onError . In diesem Fall kann der Fehler nicht RxJavaPlugins.onError und wird an RxJavaPlugins.onError

Wenn RxJava keinen grundlegenden Fehlerbehandler hat, werden solche Fehler vor uns verborgen und die Entwickler werden über mögliche Probleme im Code im Dunkeln bleiben.

Ab Version 2.0.6 versucht 2.0.6 , RxJavaPlugins.onError zu sein, und teilt Bibliotheks- / Implementierungsfehler und Situationen, in denen ein Fehler nicht RxJavaPlugins.onError werden kann. Fehler, die der Kategorie „Bugs“ zugewiesen sind, werden UndeliverableException aufgerufen, während der Rest in UndeliverableException und dann aufgerufen wird. Sie können all diese Logik hier sehen ( onError und isBug ).

Einer der RxJava Neulinge in der RxJava ist OnErrorNotImplementedException . Dieser Fehler tritt auf, wenn observable einen Fehler verursacht und die onError Methode nicht im Abonnenten implementiert ist. Dieser Fehler ist ein Beispiel für einen Fehler, der für den grundlegenden Fehlerbehandler RxJava ein "Fehler" ist und nicht zu einer UndeliverableException .

UndeliverableException


Da Fehler im Zusammenhang mit „Fehlern“ leicht zu beheben sind, werden wir uns nicht mit ihnen befassen. Die Fehler, die RxJava in eine RxJava sind interessanter, da möglicherweise nicht immer klar ist, warum der Fehler nicht an onError .

Die Fälle, in denen dies passieren kann, hängen davon ab, was die Quellen und Abonnenten speziell tun. Wir werden die folgenden Beispiele betrachten, aber im Allgemeinen können wir sagen, dass ein solcher Fehler auftritt, wenn es keinen aktiven Teilnehmer gibt, an den der Fehler geliefert werden kann.

Beispiel mit zipWith ()


Die erste Option, mit der Sie eine zipWith können, ist die 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() } ) 

Wir kombinieren zwei Quellen miteinander, von denen jede einen Fehler verursacht. Was erwarten wir? Wir können davon ausgehen, dass onError zweimal aufgerufen wird, dies widerspricht jedoch der Spezifikation für Reactive streams .
Nach einem einzelnen Aufruf eines Terminalereignisses ( onError , onCompelete ) müssen keine weiteren Anrufe mehr getätigt werden

Es stellt sich heraus, dass mit einem einzigen Aufruf von onError zweiter Aufruf nicht mehr möglich ist. Was passiert, wenn ein zweiter Fehler in der Quelle auftritt? Es wird an RxJavaPlugins.onError geliefert.

Eine einfache Möglichkeit, in diese Situation zu gelangen, besteht darin, Netzwerkanrufe mit zip zu kombinieren (z. B. zwei Retrofit Anrufe, die Observable ). Wenn bei beiden Aufrufen ein Fehler auftritt (z. B. besteht keine Internetverbindung), verursachen beide Quellen Fehler, von denen die erste in die onError Implementierung fällt und die zweite an den RxJavaPlugins.onError ( RxJavaPlugins.onError ) RxJavaPlugins.onError .

ConnectableObservable-Beispiel ohne Abonnenten


ConnectableObservable kann auch eine ConnectableObservable . Es sei daran erinnert, dass ConnectableObservable Ereignisse unabhängig von der Anwesenheit aktiver Abonnenten ConnectableObservable Sie einfach die connect() -Methode auf. Wenn in ConnectableObservable ein Fehler auftritt, wenn keine Abonnenten vorhanden sind, wird er an den ConnectableObservable .

Hier ist ein ziemlich unschuldiges Beispiel, das einen solchen Fehler verursachen könnte:

 someApi.retrofitCall() //     Retrofit .publish() .connect() 

Wenn someApi.retrofitCall() einen Fehler verursacht (z. B. besteht keine Internetverbindung), stürzt die Anwendung ab, da der Netzwerkfehler an den RxJava wird.

Dieses Beispiel scheint fiktiv zu sein, aber es ist sehr einfach, in eine Situation zu geraten, in der ConnectableObservable noch verbunden ist, aber keine Abonnenten hat. Ich bin darauf autoConnect() als autoConnect() beim Aufrufen einer API autoConnect() . autoConnect() deaktiviert Observable nicht automatisch. Ich habe die onStop Activity abgemeldet, aber das Ergebnis des Netzwerkaufrufs, der nach der Zerstörung der Activity und der Anwendung UndeliverableException mit UndeliverableException .

Fehlerbehandlung


Was tun mit diesen Fehlern?

Der erste Schritt besteht darin, die auftretenden Fehler zu untersuchen und festzustellen, was sie verursacht. Ideal, wenn Sie das Problem an der Quelle beheben können, um zu verhindern, dass der Fehler an RxJavaPlugins.onError .

Die Lösung für das Beispiel zipWith besteht darin, eine oder beide Quellen zu verwenden und eine der Methoden zum Abfangen von Fehlern in ihnen zu implementieren. Beispielsweise können Sie onErrorReturn , um den Standardwert anstelle eines Fehlers zu übergeben.

Das ConnectableObservable Beispiel ist einfacher zu beheben. ConnectableObservable Sie lediglich sicher, dass Sie Observable trennen, wenn sich der letzte Abonnent abmeldet. autoConnect() verfügt beispielsweise über eine überladene Implementierung, die eine Funktion übernimmt, die die Verbindungszeit erfasst ( mehr finden Sie hier ).

Eine andere Möglichkeit, das Problem zu lösen, besteht darin, den Basisfehlerbehandler durch Ihren eigenen zu ersetzen. Die RxJavaPlugins.setErrorHandler(Consumer<Throwable>) hilft Ihnen dabei. Wenn dies die für Sie richtige Lösung ist, können Sie alle an RxJavaPlugins.onError gesendeten Fehler RxJavaPlugins.onError und nach RxJavaPlugins.onError behandeln. Diese Lösung kann sehr kompliziert sein - denken RxJavaPlugins.onError daran, dass RxJavaPlugins.onError Fehler von allen RxJava Streams in der Anwendung empfängt.

Wenn Sie Ihre Observable manuell erstellen, können Sie emitter.tryOnError() anstelle von emitter.tryOnError() . Diese Methode meldet einen Fehler nur, wenn der Stream nicht beendet ist und Abonnenten hat. Denken Sie daran, dass diese Methode experimentell ist.

Die Moral dieses Artikels ist, dass Sie nicht sicher sein können, dass bei der Arbeit mit RxJava keine Fehler auftreten, wenn Sie einfach onError in Abonnenten implementiert onError . Sie sollten sich der Situationen bewusst sein, in denen Abonnenten möglicherweise keine Fehler zur Verfügung stehen, und sicherstellen, dass diese Situationen behandelt werden.

Source: https://habr.com/ru/post/de422611/


All Articles