我在Android应用中使用Retrofit和RxJava进行通信,并且必须处理从看似正常的HTTP响应(状态为200的代码)中解析响应时发生的错误。

我还实现了一种使用retryWhen运算符来处理错误的方法,该运算符连接到用户的输入来决定是否重试。通过重新订阅原始的Observable可以起作用。

我尝试过的第一种方法是具有以下内容:

services.getSomething()
  .map(response -> {
    if (checkBadResponse(response)) {
      throw new RuntimeException("Error on service");
    } else {
      return parseResponse(response);
    }
  }).retryWhen(this::shouldRetry);

这样就不会再次调用该服务。看来retryWhen运算子无法重新订阅服务的Observable

最终的工作是实现另一个运算符,该运算符不会向前发送onCompleted并将其与lift一起使用,如下所示:
public class CheckResponseStatus<T> implements Observable.Operator<ResponsePayload<T>, ResponsePayload<T>> {
    @Override
    public Subscriber<? super ResponsePayload<T>> call(Subscriber<? super ResponsePayload<T>> subscriber) {
        return new Subscriber<ResponsePayload<T>>() {
            private boolean hasError = false;

            @Override
            public void onCompleted() {
                if (!hasError)
                    subscriber.onCompleted();
            }

            @Override
            public void onError(Throwable e) {
                hasError = true;
                subscriber.onError(e);
            }

            @Override
            public void onNext(ResponsePayload<T> response) {
                if (response.isOk()) {
                    subscriber.onNext(response);
                } else {
                    hasError = true;
                    subscriber.onError(new RuntimeException(response.getMessage()));
                }
            }
        };
    }
}

像这样使用它:
services.getSomething()
  .lift(new CheckResponseStatus())
  .map(response -> parseResponse(response))
  .retryWhen(this::shouldRetry);

这是正确的处理方式还是有更简单,更好的方式?

最佳答案

它看起来像rx-java实现中的错误。无论如何,从map函数抛出异常是一件坏事,因为该函数应该是纯函数(例如没有副作用)。在这种情况下,您应该使用flatMap运算符:

services.getSomething()
  .flatMap(response -> {
    if (checkBadResponse(response)) {
      return Observable.<ResponseType>error(new RuntimeException("Error on service"));
    } else {
      return Observable.<ResponseType>just(parseResponse(response);
    }
  }).retryWhen(this::shouldRetry);

上面的代码按预期工作,如果发生错误,则实际上重试请求。

07-24 18:24