rxjava:避免在下游操作中取消订阅错误

时间:2017-10-30 08:12:48

标签: rx-java

我对RxJava很陌生,即使经过数小时的研究,我也找不到合适的解决方案。

这是:我正在开发一个银行应用程序,它连接到某个服务器以接收定价更新,处理这些更新,并将它们保存在某个内部数据库中。使用RxJava 2,在没有错误发生的情况下很容易获得一些工作,但是如果处理阶段出现错误,事情会变得很丑:订阅被取消,并且不再收到消息。

我曾经使用过ad-hoc流媒体库,只是捕获异常,记录导致它们的错误和消息,并保持流打开。这是一种处理错误的非常简单和实用的方法,但它确保了应用程序的相当好的健壮性。

使用RxJava2,我能想到的唯一方法就是通过捕获这些异常的包装器来包围我的所有映射运算符。它感觉不是很强大,因为任何人都可能忘记捕获错误,这会导致应用程序终止。

为了让我的案子更清楚,这是一个目前失败的测试,我想修复:

@Test
public void shouldKeepProducingWithErrorsInMap() {

    List<Integer> output = new ArrayList<>();
    List<Throwable> errors = new ArrayList<>();
    AtomicInteger startCount = new AtomicInteger(0);

    Observable<Integer> source = Observable.defer(() -> {
        startCount.incrementAndGet();
        return Observable.range(1, 10);
    });

    source
            .map(i -> {
                if (i % 3 == 0) {
                    throw new IllegalStateException("I cannot accept multiples of 3, this is " + i);
                }
                return i;
            })
            .subscribe(output::add, errors::add, () -> log.info("done"));

    Assertions.assertThat(startCount.get()).isEqualTo(1);
    Assertions.assertThat(output).containsExactly(1, 2, 4, 5, 7, 8, 10);
    Assertions.assertThat(errors).hasSize(0);

}

为了达到合适的稳健性,我想要以下内容:

  1. 通过从某些工厂方法返回Observable来封装连接到某个任意源的逻辑
  2. 避免在下游某点发生错误时重新连接到源
  3. 如果出现下游错误,只需记录错误并转到下一条消息
  4. 实施此&#34;源保护&#34;以工厂方法本身为特色,以避免在发生意外错误时中断馈送。
  5. 有没有办法在RxJava2中使用可用的运算符实现这一点,还是应该为此实现自定义运算符?

2 个答案:

答案 0 :(得分:0)

使用onErrorReturn运算符(documentation),当上游observable发出错误时,它允许您发出元素。我希望observable的结果对象也封装它失败的状态,并在失败时发出该对象。像。的东西。

someObservable
    .map(response -> SomeResult.success(action.getMessage()))
    .onErrorReturn(t -> SomeResult.failure(t.getMessage()))
    .observeOn(AndroidSchedulers.mainThread())
// e.g. do something with result, or show toast when it fails.

答案 1 :(得分:0)

给我的一个可能的答案是使用concatMap或flatMap(感谢@obogoliy,感谢您的建议!):

@Test
public void shouldCatchErrorByUsingConcatMap() {
    List<Integer> output = new ArrayList<>();
    List<Throwable> errors = new ArrayList<>();
    AtomicInteger startCount = new AtomicInteger(0);

    Observable<Integer> source = Observable.defer(() -> {
        startCount.incrementAndGet();
        return Observable.range(1, 10);
    });

    source.concatMap(j -> Observable.just(j)
            .map((i) -> {
                if (i % 3 == 0) {
                    throw new IllegalStateException("I cannot accept multiples of 3, this is " + i);
                }
                return i;
            }).onErrorResumeNext(e -> {
                log.error("got error", e);
                return Observable.empty();
            }))
            .subscribe(output::add, errors::add, () -> log.info("done"));

    Assertions.assertThat(startCount.get()).isEqualTo(1);
    Assertions.assertThat(output).containsExactly(1, 2, 4, 5, 7, 8, 10);
    Assertions.assertThat(errors).hasSize(0);
}

这是隔离可能失败的流部分,并使用onErrorResumeNext记录错误并删除消息。

它工作得很好,我能看到的唯一问题是必须在下游完成,因此无法从工厂类中发送“强大”源。但这样做可能意味着打破rxJava的一些核心契约(onError只调用一次,不会吞下错误)。

如果有人能找到更好的东西,我仍在购买;)