当前位置: 首页 > 知识库问答 >
问题:

Retrofit 2和RxJava错误处理操作符

朱修德
2023-03-14

我在我的项目中使用Retrofit2与可观察的接口和结果包装器。示例

@POST("api/login")
Observable<Result<LoginResponse>> login(@Body LoginRequest request);

我需要Result wrapper从响应中获得比序列化对象更多的信息(例如headers、http Status...)。

问题是,对于结果包装器,网络调用不会引发异常。您可以通过调用result.error()在结果中找到异常。

如果我想利用RxJava错误操作符,我应该怎么做?例如,我希望对网络错误使用重试操作符,但重试操作符仅在由Observable引发异常时才起作用。

共有1个答案

那谦
2023-03-14

这是我想出的解决办法。如果我会改进它,我会在这里张贴的变化。

我的问题的解决方案(exception被Retrofit吞掉,不由RxJava处理)是observable.error方法,它创建一个新的observable,只发出错误,所以我可以“重新抛出”异常。

我创建了一个可观察的转换器,以追加到每个发出retrofit.result的rest调用。该转换器接受一个可观察的>,如果响应没有错误,则将其转换为一个可观察的>。如果有错误,它返回一个带有自定义HTTP*Exceptions的Observerable.Error。我把它作为一个名为ObservableTransformations.ResultToResponseWithHttperRorHandling的实用程序类的静态方法。

public class ObservableTransformations {

public static <T> Observable.Transformer<Result<T>, Response<T>> resultToResponseWithHttpErrorHandling() {
    return observable -> observable.flatMap(r -> {
        Observable<Response<T>> returnObservable = Observable.just(r.response());
        if (r.isError()) {
            Throwable throwable = r.error();
            if (throwable instanceof IOException) {
                Timber.e(throwable, "Retrofit connection error.");
                // TODO Check this cases
                if (throwable instanceof java.net.ConnectException) {
                    returnObservable = Observable.error(new HttpNoInternetConnectionException());
                } else if (throwable instanceof SocketTimeoutException) {
                    returnObservable = Observable.error(new HttpServerDownException());
                } else {
                    returnObservable = Observable.error(new HttpNoInternetConnectionException());
                }
            } else {
                Timber.e(throwable, "Retrofit general error - fatal.");
                returnObservable = Observable.error(new HttpGeneralErrorException(r.error()));
            }
        } else {
            Response<T> retrofitResponse = r.response();
            if (!retrofitResponse.isSuccess()) {
                int code = retrofitResponse.code();
                String message = "";
                try {
                    message = retrofitResponse.errorBody().string();
                } catch (IOException e) {
                    Timber.e(e, "Error reading errorBody from response");
                }
                Timber.i("Server responded with error. Code: " + code + " message: " + message);
                Throwable t = null;
                if (NetworkUtils.isClientError(code)) {
                    t = new HttpClientException(retrofitResponse.code(), message);
                } else if (NetworkUtils.isServerError(code)) {
                    t = new HttpServerErrorException(retrofitResponse.code(), message);
                }
                returnObservable = Observable.error(t);
            }
        }
        return returnObservable;
    }).retryWhen(new RetryWithDelayIf(3, 1000, t -> {
        return (t instanceof HttpNoInternetConnectionException) || (t instanceof HttpServerDownException);
    }));
}

}

仅当异常为HttpNoInternetConnectionException或HttpServerDownException时,才使用指数后退重试3次。

RetryWithDelayIf类在这里。它将重试要满足的条件作为构造函数的最后一个参数(一个函数,接受一个throwable,如果这个throwable应该触发重试,则返回true,如果没有,则返回false)。

public class RetryWithDelayIf implements
    Func1<Observable<? extends Throwable>, Observable<?>> {

private final int maxRetries;
private final int retryDelayMillis;
private int retryCount;
private Func1<Throwable, Boolean> retryIf;

public RetryWithDelayIf(final int maxRetries, final int retryDelayMillis, Func1<Throwable, Boolean> retryIf) {
    this.maxRetries = maxRetries;
    this.retryDelayMillis = retryDelayMillis;
    this.retryCount = 0;
    this.retryIf = retryIf;
}

@Override
public Observable<?> call(Observable<? extends Throwable> attempts) {
    return attempts.zipWith(Observable.range(1, maxRetries + 1), (n, i) -> {
        return new Tuple<Throwable, Integer>(n, i);
    })
            .flatMap(
                    ni -> {
                        if (retryIf.call(ni.getFirst()) && ni.getSecond() <= maxRetries) {
                            return Observable.timer((long) Math.pow(2, ni.getSecond()), TimeUnit.SECONDS);
                        } else {
                            return Observable.error(ni.getFirst());
                        }
                    });
}

}

最后,下面是restService调用的用法:

restService.login(new LoginRestRequest(username, password))
                .compose(ObservableTransformations.resultToResponseWithHttpErrorHandling());

在观察者的onError中,您最终可以处理HTTP*Exceptions。

 类似资料:
  • 有两种主要的方法来处理流中的错误。你可以重试流并保证流最终会正常运行,或者处理错误并进行转换。 重试 - 现在怎么样? 当你认为错误是由于某些原因是暂时导致的,那么这种方法是适用的。通常不稳定的网络是个很好的例子。当网络不稳定时端点可能会在你多次尝试后才能回应。要点是你的首次尝试可能失败,但重试x次并且在两次尝试之间有一定的时间间隔,最终端点会回应。 retry retry() 操作符可以让我们重

  • 我正在使用较新的RX java,而不是 如果一切正常的话,这段代码几乎可以完美地工作。但如果我故意犯了个错误,比如我关掉了WiFi。而不是改型获得“UnknownHostException”...因为它应该发生在try catch块中的reverfit调用(getUser)中。但它没有将错误处理到onError(Throwable t)-->(我可以处理的地方),而是使应用程序崩溃。这就像错误从未

  • 一般来说,Observable不会抛异常。它会调用 onError 终止Observable序列,以此通知所有的观察者发生了一个不可恢复的错误。 但是,也存在一些异常。例如,如果 onError 调用失败了,Observable不会尝试再次调用 onError 去通知观察者,它会抛出 RuntimeException,OnErrorFailedException 或者 OnErrorNotImpl

  • 1.1. 总体概览 The Hyperledger Fabric error handling framework can be found in the source repository under common/errors. It defines a new type of error, CallStackError, to use in place of the standard err

  • 我有一个向服务器发送数据的调用。如果一切正常,服务器会返回一个无效的响应。但是当有错误时,我会得到数据。 当有错误时,为了管理这个情况,我有一个对象。问题是,当请求成功时,改造会抛出一个异常,消息是:

  • 我有这个问题关于空数据库返回与房间和RxJava单一。我知道我的数据库是空的,所以我希望在触发时得到一个空的返回 问题是当我调用这个函数如下所示时 但仍然返回异常和崩溃 w/system.err:io.reactivex.exceptions.onerrornotimplementedexception:查询返回空结果集:SELECT*FROM Times WHERE timestamp=? 我在