2015-11-16 16 views
12

Używam Retrofit 2 w moim projekcie z interfejsem Observable i wrapper Result. Przykład:Operatory obsługi błędów Retrofit 2 i RxJava

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

muszę opakowanie Wynik aby uzyskać więcej informacji z odpowiedzi niż tylko serializowanym obiektu (na przykład nagłówki, stanu HTTP ...).

Problem polega na tym, że w opakowaniu wyników nie jest zgłaszany wyjątek przez połączenie sieciowe. Możesz znaleźć wyjątek w wyniku, wywołując funkcję Result.error().

Co należy zrobić, jeśli chcę skorzystać z operatorów błędów RxJava? Na przykład chciałbym użyć operatora ponowienia na błąd sieci, ale operator ponawiania działa tylko wtedy, gdy wyjątek jest zgłaszany przez obserwowalne.

+0

Zobacz link http://bytes.babbel.com/pl/artykuly/2016-03-16-retrofit2-rxjava-error-handling.html Opisują one, jak nadpisać _RxJavaCallAdapterFactory_ w _Retrofit.Builder_ i tam dla możesz łapać swoje obiekty w _Observable.onResumeErrorNext_/_Observable.onDoNext() _ – murt

Odpowiedz

13

Oto rozwiązanie, które wymyśliłem. Jeśli to poprawię, opublikuję zmiany tutaj.

Rozwiązanie mojego problemu (wyjątek połkniętego przez Retrofit i nie obsługiwane przez RxJava) jest metoda Observable.error że tworzy nowy zaobserwować, że tylko emituje ten błąd, więc mogę „przekaż” wyjątek.

Stworzyłem obserwowalny transformator, który będzie dołączany do każdego połączenia, które wyemituje doposażenie.Wyniki. Ten transformator przyjmuje obserwowalne> i, jeśli odpowiedź nie zawiera błędów, przekształca go w Obserwowalne>. Jeśli występują błędy, zwraca obiekt Observable.error z niestandardowymi wyjątkami Http *, które mogę później obsłużyć w moim obserwatorze w wywołaniu zwrotnym onError. Ustanawiam to jako statyczną metodę klasy narzędziowej o nazwie ObservableTransformations.resultToResponseWithHttpErrorHandling.

Tutaj jest on:

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

} 

Ponowna próba jest 3 razy przy użyciu odczekiwanie wykładniczym, a tylko wtedy, gdy wyjątek HttpNoInternetConnectionException lub HttpServerDownException.

Klasa RetryWithDelayIf jest tutaj. Warunek jest spełniony, aby ponowić próbę, jako ostatni argument konstruktora (funkcja pobierająca i zwracającą wartość true, jeśli ta opcja powinna wywołać ponowną próbę i wartość false, jeśli nie).

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

} 

Wreszcie, tutaj jest wykorzystanie z rozmowy restService:

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

W onError swojego obserwatora można wreszcie obsługiwać HTTP * Wyjątki.

+0

Wygląda jak rozwiązanie, które może być pomocne dla mojego problemu.Proszę zobaczyć moje pytanie i może wymyślimy dobre ogólne rozwiązanie dla dobra społeczności. http://stackoverflow.com/questions/39437299/rxjava-retrofit-baseobservable-for-api-calls-for-centralized-response-handl – GuyZ

0

Należy sprawdzić, czy rzut Throwable jest instancją wyjątku HttpException.

+0

Przykro mi, ale myślę, że nie zrozumiałeś problemu. Przy okazji, Retrofit 2 teraz generuje ogólny wyjątek IOException, jeśli nie może dotrzeć do serwera (brak połączenia z Internetem, serwer nie działa). – Ena

Powiązane problemy