2015-11-16 40 views
12

Tôi đang sử dụng Retrofit 2 trong dự án của mình với giao diện Quan sát và trình bao bọc Kết quả. Ví dụ:Trang bị thêm 2 và các toán tử xử lý lỗi RxJava

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

Tôi cần trình bao bọc Kết quả để lấy thêm thông tin từ phản hồi hơn chỉ là đối tượng được tuần tự hóa (ví dụ: tiêu đề, trạng thái http ...).

Vấn đề là, với trình bao bọc Kết quả không có ngoại lệ nào được thực hiện bởi cuộc gọi mạng. Bạn có thể tìm thấy ngoại lệ bên trong kết quả bằng cách gọi Result.error().

Tôi nên làm gì nếu tôi muốn tận dụng lợi thế của các nhà khai thác lỗi RxJava? Ví dụ: Ví dụ, tôi muốn sử dụng toán tử thử lại trên một lỗi mạng, nhưng toán tử thử lại chỉ hoạt động nếu một ngoại lệ được phát hiện bởi quan sát được.

+0

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

Trả lời

13

Đâ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ệ.

+0

Trông giống như một giải pháp có thể hữu ích cho vấn đề của tôi.Xin vui lòng xem câu hỏi của tôi và có lẽ chúng tôi có thể đưa ra một giải pháp chung chung tốt đẹp vì lợi ích của cộng đồng. http://stackoverflow.com/questions/39437299/rxjava-retrofit-baseobservable-for-api-calls-for-centralized-response-handl – GuyZ

0

Bạn nên kiểm tra xem Throw Throw có phải là một thể hiện của HttpException hay không.

+0

Tôi xin lỗi nhưng tôi nghĩ bạn không hiểu vấn đề. Bằng cách này, Retrofit 2 bây giờ ném một IOException chung nếu nó không thể tiếp cận với máy chủ (không có kết nối internet, máy chủ xuống ..). – Ena

Các vấn đề liên quan