Why does my RxJava Observable emit only to the first consumer?

♀尐吖头ヾ 提交于 2019-12-08 05:55:33

问题


Can someone explain why the below test fails?

public class ObservableTest {
    @Test
    public void badObservableUsedTwiceDoesNotEmitToSecondConsumer() {
        // Any simpler observable makes the test pass
        Observable<Integer> badObservable = Observable.just(1)
                .zipWith(Observable.just(2), (one, two) -> Observable.just(3))
                .flatMap(observable -> observable);

        ObservableCalculator calc1 = new ObservableCalculator(badObservable);
        ObservableCalculator calc2 = new ObservableCalculator(badObservable);

        // zipping causes the failure
        // Calling calculate().toBlocking().subscribe() on each calc passes
        // Observable.from(listOfCalcs).flatMap(calc -> calc.calculate()) passes
        Observable.zip(ImmutableList.of(calc1.calculate(), calc2.calculate()), results -> results)
                .toBlocking()
                .subscribe();

        assertThat(calc1.hasCalculated).isTrue();
        assertThat(calc2.hasCalculated).isTrue(); // this fails
    }

    private static class ObservableCalculator {
        private final Observable<?> observable;

        public boolean hasCalculated = false;

        public ObservableCalculator(Observable<?> observable) {
            this.observable = observable;
        }

        public Observable<Void> calculate() {
            return observable.concatMap(o -> {
                hasCalculated = true;
                // returning Observable.just(null) makes the test pass
                return Observable.empty();
            });
        }
    }
}

I've tried to simplify the "bad" observable further, but can't find anything I can remove to make it simpler.

My current understanding, though, is that it's an Observable which (regardless of how it's constructed), should emit a single value and then complete. We then make two similar instances of an object based on that Observable, and call a method on those objects which consumes the Observable, makes a note of having done so, and then returns Observable.empty().

Can anyone explain why using this observable causes the test the fail (when using a simpler observable causes the test to pass)?

It's also possible to make the test pass by either serially calling calculate().toBlocking().subscribe() rather than using zip, or making calculate return Observable.just(null) instead. That makes some sense to me (zip won't subscribe to calc2 if calc1 is empty, since it in that case zip could never yield anything), but not complete sense (I don't understand why zip doesn't behave like that for a simpler version of badObservable - the calculate() methods still return empty, regardless of that input).


回答1:


If you zip an empty source with something, the operator detects it can't produce any value anymore and unsubscribes from all of its sources. There is a mix of zip and merge involved and merge takes unsubscription seriously: it doesn't emit the value 3 at all thus concatMap doesn't call the mapping function for the second source either.



来源:https://stackoverflow.com/questions/34301062/why-does-my-rxjava-observable-emit-only-to-the-first-consumer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!