Đây là giải pháp mà tôi đã đưa ra. Nếu tôi cải thiện nó, tôi sẽ đăng các thay đổi ở đây.
Giải pháp cho vấn đề của tôi (ngoại lệ bị nuốt bởi Retrofit và không được xử lý bởi RxJava) là phương pháp Observable.error tạo ra một quan sát mới chỉ phát ra lỗi, vì vậy tôi có thể "rút lại" ngoại lệ.
Tôi đã tạo một biến áp quan sát để nối thêm vào mọi cuộc gọi còn lại phát ra một retrofit.Result. Biến áp này có một Observable> và, nếu đáp ứng không có lỗi, biến nó thành một Observable>. Nếu có lỗi, nó trả về một Observable.error với tùy chỉnh Http * Ngoại lệ mà sau này tôi có thể xử lý trong Trình quan sát của tôi trong lời gọi onError. Tôi đặt nó làm phương thức tĩnh của lớp tiện ích được gọi là ObservableTransformations.resultToResponseWithHttpErrorHandling.
Ở đây là:
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);
}));
}
}
Các retry được làm 3 lần sử dụng một backoff mũ, và chỉ khi ngoại lệ là HttpNoInternetConnectionException hoặc HttpServerDownException.
Lớp Thử lạiWithDelayNếu có tại đây. Nó có điều kiện để được đáp ứng cho thử lại như là đối số cuối cùng của hàm tạo (một hàm lấy một throwable và return true nếu throwable này sẽ kích hoạt retry và false nếu không).
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());
}
});
}
}
Cuối cùng, đây là việc sử dụng với một cuộc gọi restService:
restService.login(new LoginRestRequest(username, password))
.compose(ObservableTransformations.resultToResponseWithHttpErrorHandling());
Trong onerror của quan sát viên của bạn, bạn cuối cùng có thể xử lý các Http * Trường hợp ngoại lệ.
Nhìn vào liên kết http://bytes.babbel.com/en/articles/2016-03-16-retrofit2-rxjava-error-handling.html Chúng mô tả cách ghi đè _RxJavaCallAdapterFactory_ trong _Retrofit.Builder_ và ở đó cho bạn có thể bắt các đối tượng của bạn trong _Observable.onResumeErrorNext_/_Observable.onDoNext() _ – murt