Operadores de manipulación de errores de Retrofit 2 y RxJava

Estoy utilizando Retrofit 2 en mi proyecto con la interfaz Observable y el envoltorio Result. Ejemplo:

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

Necesito el contenedor de resultados para obtener más información de la respuesta que sólo el objeto serializado (por ejemplo encabezados, estado http …).

El problema es que, con el contenedor de resultados, no se produce ninguna excepción por la llamada de red. Puede encontrar la excepción dentro del resultado llamando a Result.error ().

¿Qué debo hacer si quiero aprovechar los operadores de error de RxJava? Por ejemplo, me gustaría utilizar el operador de reintento en un error de red, pero el operador de reintento sólo funciona si una excepción es lanzada por el observable.

Aquí está la solución que me ocurrió. Si lo mejoraré, publicaré los cambios aquí.

La solución a mi problema (excepción tragada por Retrofit y no manejada por RxJava) es el método Observable.error que crea una nueva observable que sólo emite el error, por lo que puedo "volver a" la excepción.

Creé un transformador observable para añadir a cada llamada de descanso que emite un retrofit.Result. Este transformador toma un Observable> y, si la respuesta no tiene errores, lo transforma en un Observable>. Si hay errores, devuelve un Observable.error con Http personalizado * Excepciones que puedo manejar más tarde en mi Observer en la devolución de llamada onError. Lo pongo como un método estático de una clase de utilidad llamada ObservableTransformations.resultToResponseWithHttpErrorHandling .

Aquí está:

 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); })); } } 

El reintento se realiza 3 veces utilizando un backoff exponencial, y sólo si la excepción es HttpNoInternetConnectionException o HttpServerDownException.

La clase RetryWithDelayIf está aquí. Se toma la condición a ser cumplida para el reintento como el último argumento del constructor (una función que toma un throwable y que devuelve verdadero si este throwable debe activar el retry y false si no).

 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()); } }); } } 

Por último, aquí está el uso con una llamada restService:

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

En el onError de su observador finalmente puede manejar las Excepciones Http *.

Debe comprobar si Throwable lanzado es una instancia de HttpException.

  • Retrofit 2 + Rxjava error de manipulación
  • Android AsyncTask vs hilo + controlador vs rxjava
  • Cómo utilizar Realm asObservable con el operador concat () de RxJava?
  • RxAndroid: cambios de interfaz de usuario en el subproceso Schedulers.io ()
  • Cancelar la suscripción de un rx.Single en RxJava
  • Rxandroid pide que se ejecute en el hilo ui aunque esté suscrito en AndroidSchedulers.mainThread ()
  • Cómo manejar clics de artículo para una vista de reciclar usando RxJava
  • Cómo obtener un Desechable para sujetos como suscriptor en RxJava2
  • Realm, RxJava, asObservable () y doOnUnsubscribe ()
  • Retrofit2 y kotlin
  • Android rxjava ordenar la lista con la clase de comparación
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.