Operatori di gestione degli errori Retrofit 2 e RxJava

Sto utilizzando Retrofit 2 nel mio progetto con l'interface Osservabile e il wrapper Risultato. Esempio:

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

Ho bisogno del wrapper Result per get ulteriori informazioni dalla risposta che non solo l'object serializzato (ad esempio intestazioni, stato http …).

  • Android: Imansible istanziare l'attività / ClassNotFoundException
  • Immagine scattata dalla camera o dalla galleria quando si utilizza nell'image il suo orientamento che viene modificato e talvolta verticalmente allungato in Android
  • Selettore di posizione Chiudere automaticamente dopo il lancio
  • com.google.android.gms.common.internal.safe parcel.safe parcelle non trovate
  • Come faccio a creare un ListView con angoli arrotondati in Android?
  • Vista di elenco Android con separatori
  • Il problema è che, con il wrapper Result, non viene generata alcuna exception dalla chiamata di networking. È ansible trovare l'exception all'interno del risultato chiamando Result.error ().

    Cosa devo fare se voglio approfittare degli operatori di errori RxJava? Ad esempio, vorrei utilizzare l'operatore del tentativo su un errore di networking, ma l'operatore di ripetizione funziona solo se viene generata un'exception dall'osservabile.

  • android: webview all'interno di dialogo o popup
  • La tattica della canvas di canapa crea il problema in phonegap
  • Errore manifesto di fusione manifesta
  • Terminare la vecchia attività e avviare una nuova o viceversa
  • Frammento di Android suCreateView con i gesti
  • Android OKHttp aggiungendo params
  • 2 Solutions collect form web for “Operatori di gestione degli errori Retrofit 2 e RxJava”

    Ecco la soluzione che ho scoperto. Se lo migliorerò, pubblicherò qui i cambiamenti.

    La soluzione al mio problema (exception inghiottita da Retrofit e non gestita da RxJava) è il metodo Observable.error che crea una nuova osservabile che emette solo l'errore, in modo che posso "riprendere" l'exception.

    Ho creato un trasformatore osservabile da appendere a each chiamata di rest che emette un retrofit.Result. Questo trasformatore prende un Observable> e, se la risposta non ha errori, lo trasforma in un Observable>. Se ci sono errori, restituisce un Observable.error con eccezioni Http * personalizzate che posso gestire in seguito nel mio Observer nella callback onError. Lo ho messo come metodo statico di una class di utilità chiamata ObservableTransformations.resultToResponseWithHttpErrorHandling .

    Ecco qui:

     public class ObservableTransformations { public static <T> Observable.Transformsr<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); })); } } 

    Il tentativo viene effettuato 3 volte utilizzando un backoff esponenziale e solo se l'exception è HttpNoInternetConnectionException o HttpServerDownException.

    La class RetryWithDelayIf è qui. Prende la condizione di essere soddisfatta per riprovare come ultimo argomento del constructor (una function che assume un throwable e restituisce true se questo throwable dovrebbe triggersre il tentativo e falso se 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()); } }); } } 

    Infine, ecco l'utilizzo con una chiamata restService:

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

    In onError del tuo osservatore puoi finalmente gestire le eccezioni Http *.

    Dovresti verificare se il lancio Throwable è un'istanza di HttpException.

    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.