Retrofit 2 和 RxJava 错误处理运算符
Retrofit 2 and RxJava error handling operators
我在我的项目中使用带有 Observable 接口和结果包装器的 Retrofit 2。示例:
@POST("api/login")
Observable<Result<LoginResponse>> login(@Body LoginRequest request);
我需要结果包装器从响应中获取更多信息,而不仅仅是序列化的 object(例如 headers、http 状态...)。
问题是,对于结果包装器,网络调用不会抛出异常。您可以通过调用 Result.error().
在结果中找到异常
想利用RxJava错误算子怎么办?
例如,我想在出现网络错误时使用重试运算符,但重试运算符仅在可观察对象抛出异常时才起作用。
您应该检查抛出的 Throwable 是否是 HttpException 的实例。
这是我想出的解决方案。
如果我要改进它,我会 post 这里的更改。
我的问题的解决方案(异常被 Retrofit 吞没,而不被 RxJava 处理)是 Observable.error 方法,它创建一个只发出错误的新可观察对象,所以我可以"rethrow"例外。
我创建了一个可观察的转换器来附加到每个发出 retrofit.Result 的 rest 调用。
这个转换器接受一个 Observable>,如果响应没有错误,就把它转换成一个 Observable>。如果有错误,它 returns 一个带有自定义 Http*Exceptions 的 Observable.error,我稍后可以在我的 Observer 的 onError 回调中处理。
我把它作为名为 ObservableTransformations.resultToResponseWithHttpErrorHandling.
的实用程序 class 的静态方法
这里是:
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);
}));
}
}
使用指数退避算法重试 3 次,并且仅当异常为 HttpNoInternetConnectionException 或 HttpServerDownException 时。
RetryWithDelayIf class 在这里。它将满足重试的条件作为构造函数的最后一个参数(一个函数接受一个 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。
我在我的项目中使用带有 Observable 接口和结果包装器的 Retrofit 2。示例:
@POST("api/login")
Observable<Result<LoginResponse>> login(@Body LoginRequest request);
我需要结果包装器从响应中获取更多信息,而不仅仅是序列化的 object(例如 headers、http 状态...)。
问题是,对于结果包装器,网络调用不会抛出异常。您可以通过调用 Result.error().
在结果中找到异常想利用RxJava错误算子怎么办? 例如,我想在出现网络错误时使用重试运算符,但重试运算符仅在可观察对象抛出异常时才起作用。
您应该检查抛出的 Throwable 是否是 HttpException 的实例。
这是我想出的解决方案。 如果我要改进它,我会 post 这里的更改。
我的问题的解决方案(异常被 Retrofit 吞没,而不被 RxJava 处理)是 Observable.error 方法,它创建一个只发出错误的新可观察对象,所以我可以"rethrow"例外。
我创建了一个可观察的转换器来附加到每个发出 retrofit.Result 的 rest 调用。 这个转换器接受一个 Observable>,如果响应没有错误,就把它转换成一个 Observable>。如果有错误,它 returns 一个带有自定义 Http*Exceptions 的 Observable.error,我稍后可以在我的 Observer 的 onError 回调中处理。 我把它作为名为 ObservableTransformations.resultToResponseWithHttpErrorHandling.
的实用程序 class 的静态方法这里是:
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);
}));
}
}
使用指数退避算法重试 3 次,并且仅当异常为 HttpNoInternetConnectionException 或 HttpServerDownException 时。
RetryWithDelayIf class 在这里。它将满足重试的条件作为构造函数的最后一个参数(一个函数接受一个 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。