web-dev-qa-db-fra.com

RxJava2 prise observable jette UndeliverableException

Si je comprends bien, RxJava2 values.take(1) crée un autre observable qui ne contient qu'un élément de l'observable d'origine. Ce qui DOIT PAS émet une exception car elle est filtrée par l'effet de take(1) comme c'est arrivé en second.

comme dans le suite extrait de code

    Observable<Integer> values = Observable.create(o -> {
        o.onNext(1);
        o.onError(new Exception("Oops"));
    });

    values.take(1)
            .subscribe(
                    System.out::println,
                    e -> System.out.println("Error: " + e.getMessage()),
                    () -> System.out.println("Completed")
            );

Sortie

1
Completed
io.reactivex.exceptions.UndeliverableException: Java.lang.Exception: Oops
    at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.Java:366)
    at io.reactivex.internal.operators.observable.ObservableCreate$CreateEmitter.onError(ObservableCreate.Java:83)
    at ch02.lambda$main$0(ch02.Java:28)
    at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.Java:40)
    at io.reactivex.Observable.subscribe(Observable.Java:10841)
    at io.reactivex.internal.operators.observable.ObservableTake.subscribeActual(ObservableTake.Java:30)
    at io.reactivex.Observable.subscribe(Observable.Java:10841)
    at io.reactivex.Observable.subscribe(Observable.Java:10827)
    at io.reactivex.Observable.subscribe(Observable.Java:10787)
    at ch02.main(ch02.Java:32)
Caused by: Java.lang.Exception: Oops
    ... 8 more
Exception in thread "main" io.reactivex.exceptions.UndeliverableException: Java.lang.Exception: Oops
    at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.Java:366)
    at io.reactivex.internal.operators.observable.ObservableCreate$CreateEmitter.onError(ObservableCreate.Java:83)
    at ch02.lambda$main$0(ch02.Java:28)
    at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.Java:40)
    at io.reactivex.Observable.subscribe(Observable.Java:10841)
    at io.reactivex.internal.operators.observable.ObservableTake.subscribeActual(ObservableTake.Java:30)
    at io.reactivex.Observable.subscribe(Observable.Java:10841)
    at io.reactivex.Observable.subscribe(Observable.Java:10827)
    at io.reactivex.Observable.subscribe(Observable.Java:10787)
    at ch02.main(ch02.Java:32)
Caused by: Java.lang.Exception: Oops
    ... 8 more

Mes questions :

  1. Est-ce que je comprends bien?
  2. Qu'est-ce qui se passe réellement pour provoquer l'exception.
  3. Comment résoudre cela du consommateur?
35
AbdElraouf Sabri
  1. Oui, mais parce que les "extrémités" observables ne signifient pas que le code exécuté à l'intérieur de create(...) est arrêté. Pour être totalement sûr dans ce cas, vous devez utiliser o.isDisposed() pour voir si l'observable s'est terminé en aval.
  2. L'exception est là car RxJava 2 a la politique de ne jamais permettre à un appel onError d'être perdu. Il est soit livré en aval, soit lancé globalement UndeliverableException si l'observable est déjà terminé. Il appartient au créateur de l'observable de gérer "correctement" le cas où l'observable est terminé et qu'une exception se produit.
  3. Le problème est que le producteur (Observable) et le consommateur (Subscriber) sont en désaccord sur la fin du flux. Puisque le producteur survit au consommateur dans ce cas, le problème ne peut être résolu que chez le producteur.
51
Kiskae

@Kiskae dans le commentaire précédent a correctement répondu à la raison pour laquelle une telle exception peut se produire.

Voici le lien vers la documentation officielle sur ce thème: RxJava2-wiki .

Parfois, vous ne pouvez pas modifier ce comportement, il existe donc un moyen de gérer ces UndeliverableException. Voici un extrait de code expliquant comment éviter les accidents et les problèmes de comportement:

RxJavaPlugins.setErrorHandler(e -> {
    if (e instanceof UndeliverableException) {
        e = e.getCause();
    }
    if ((e instanceof IOException) || (e instanceof SocketException)) {
        // fine, irrelevant network problem or API that throws on cancellation
        return;
    }
    if (e instanceof InterruptedException) {
        // fine, some blocking code was interrupted by a dispose call
        return;
    }
    if ((e instanceof NullPointerException) || (e instanceof IllegalArgumentException)) {
        // that's likely a bug in the application
        Thread.currentThread().getUncaughtExceptionHandler()
            .handleException(Thread.currentThread(), e);
        return;
    }
    if (e instanceof IllegalStateException) {
        // that's a bug in RxJava or in a custom operator
        Thread.currentThread().getUncaughtExceptionHandler()
            .handleException(Thread.currentThread(), e);
        return;
    }
    Log.warning("Undeliverable exception received, not sure what to do", e);
});

Ce code provient du lien ci-dessus.

Note importante. Cette approche définit le gestionnaire d'erreurs global sur RxJava. Par conséquent, si vous pouviez vous débarrasser de ces exceptions, ce serait une meilleure option.

11
Ilia Kurtov