web-dev-qa-db-ja.com

RxJava2オブザーバブルテイクスローUndeliverableException

私が理解しているように、RxJava2 values.take(1)は、元のObservableの要素を1つだけ含む別のObservableを作成します。どのMUST NOTは、2番目に発生したtake(1)の効果によって除外されるため、例外をスローします。

followingコードスニペットのように

    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")
            );

出力

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

私の質問:

  1. 私はそれが正しいと理解していますか?
  2. 実際に例外が発生する原因は何ですか。
  3. 消費者からこれを解決するには?
35
  1. はい。ただし、観測可能な「終了」はcreate(...)内で実行されているコードが停止することを意味しないためです。この場合に完全に安全にするには、o.isDisposed()を使用して、オブザーバブルがダウンストリームで終了したかどうかを確認する必要があります。
  2. RxJava 2にはonError呼び出しが失われることを許可しないというポリシーがあるため、例外があります。オブザーバブルが既に終了している場合、ダウンストリームに配信されるか、グローバルUndeliverableExceptionとしてスローされます。 Observableの作成者は、observableが終了して例外が発生した場合を「適切に」処理する必要があります。
  3. 問題は、ストリームの終了時にプロデューサー(Observable)とコンシューマー(Subscriber)が一致しないことです。この場合、プロデューサーはコンシューマーよりも長生きしているため、プロデューサーでのみ問題を修正できます。
51
Kiskae

前のコメントの@Kiskaeは、このような例外が発生する理由について正しく答えました。

このテーマに関する公式ドキュメントへのリンク: RxJava2-wiki

この動作を変更できない場合があるため、このUndeliverableExceptionを処理する方法があります。以下は、クラッシュと誤動作を回避する方法のコードスニペットです。

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);
});

上記のリンクから取られたこのコード。

重要な注意点。このアプローチは、グローバルエラーハンドラをRxJavaに設定するため、これらの例外を取り除くことができる場合は、より良いオプションです。

11
Ilia Kurtov