2015-05-10 20 views
5

Tôi muốn trang bị thêm để nâng cao ngoại lệ tùy chỉnh tùy thuộc vào phản hồi của máy chủ. Ví dụ trong cấu trúc sau:RxJava và trang bị thêm - Tăng ngoại lệ tùy chỉnh tùy thuộc vào phản hồi của máy chủ

{ 
"code":0, 
"message":"OK", 
"data":{....} 
} 

Tôi muốn nâng cao một ngoại lệ cho các thuê bao nếu code là bất cứ điều gì khác hơn là 0. Làm thế nào là nó có thể sử dụng Retrofit và Rx? Tôi rất thích viết logic này chỉ một lần và áp dụng cho tất cả các quan sát được trả lại bằng cách trang bị thêm.

Trả lời

6

Tôi muốn nêu ngoại lệ cho người đăng ký nếu mã là bất kỳ điều gì khác ngoài 0. Làm thế nào để sử dụng Retrofit và Rx?

Bạn có thể sử dụng một nhà điều hành Observable.flatMap:

api.request().flatMap(response -> { 
    if (response.getCode() != 0) { 
     return Observable.error(new Exception("Remote error occurred")); 
    } 

    return Observable.just(response); 
}); 

Tôi rất muốn viết logic này chỉ một lần và có nó áp dụng cho tất cả các quan sát được trả về bởi retrofit.

Thật không may, không có cách nào để thực hiện việc đó bằng cách sử dụng retrofitrx-java. Bạn để viết mã ở trên cho mỗi cuộc gọi retrofit. Điều duy nhất bạn có thể làm là sử dụng phương pháp Observable.compose và giảm số lượng bản mẫu mà bạn thực sự phải viết.

api.request().compose(new ResponseTransformer<Response>()); 

Và đây là lớp ResponseTransformer:

public static class ResponseTransformer<T extends Response> implements Observable.Transformer<T, T> { 
    @Override 
    public Observable<T> call(final Observable<T> observable) { 
     return observable.flatMap(response -> { 
      if (response.getCode() != 0) { 
       return Observable.error(new Exception("Remote error occurred")); 
      } 

      return Observable.just(response); 
     }); 
    } 
} 

CẬP NHẬT

Vâng, như tôi đã nói, không có cách nào để tránh mã boilerplate chỉ sử dụng retrofitrxjava, nhưng bạn có thể giải quyết vấn đề này với dynamic proxies (lưu ý rằng bạn không cần phải gọi compose nữa):

final Api api = restAdapter.create(Api.class); 

final ClassLoader loader = api.getClass().getClassLoader(); 
final Class<?>[] interfaces = api.getClass().getInterfaces(); 

final Api proxy = (Api) Proxy.newProxyInstance(loader, interfaces, new ResponseInvocationHandler(api)); 

proxy.request().subscribe(response -> { 
    System.out.println("Success!"); 
}); 

ResponseInvocationHandler lớp:

public static class ResponseInvocationHandler implements InvocationHandler { 
    private final Object target; 

    public ResponseInvocationHandler(final Object target) { 
     this.target = target; 
    } 

    @Override 
    @SuppressWarnings({"unchecked"}) 
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { 
     final Object result = method.invoke(target, args); 

     if (result instanceof Observable) { 
      return Observable.class.cast(result).compose(new ResponseTransformer<>()); 
     } 

     return result; 
    } 
} 
+0

Cảm ơn, rất hữu ích – Nima

+1

@Nima kiểm tra câu trả lời của tôi cập nhật, nó có thể thậm chí còn hữu ích hơn. –

1

tôi sẽ đề nghị một cách tiếp cận khác nhau.

Bạn sẽ cần phải triển khai ứng dụng khách OkHttp tùy chỉnh với Trình chặn đánh dấu tùy chỉnh.

 OkHttpClient client = new OkHttpClient(); 
     client.interceptors().add(new MyInterceptor()); 
     mAdapter = new RestAdapter.Builder().setEndpoint(Consts.ENDPOINT).setClient(new OkClient(client)) 
       .setLogLevel(RestAdapter.LogLevel.BASIC).build(); 

Trong trình chặn của bạn tùy thuộc vào mã được trả về, bạn có thể tiến hành bình thường hoặc ném ngoại lệ.

Something như thế này:

public class MyInterceptor implements Interceptor { 

    @Override 
    public Response intercept(Chain chain) throws IOException { 
     Response response = chain.proceed(chain.request()); 

     if(response.code() == 0) { 
      throw new RuntimeException("Something went wrong!"); 
     } 

     return response; 
    } 
} 
+0

Nghe có vẻ như một giải pháp đầy hứa hẹn, nhưng có một vấn đề lớn với nó. 'okhttp' interceptors làm việc trên mức' http' và giá trị trả về bởi 'response.code()' thực sự là một mã http, không phải mã từ 'json'. Để trích xuất mã thực sự, bạn phải phân tích cú pháp json (đó là ý tưởng tồi bởi vì sau đó nó sẽ được phân tích cú pháp một lần nữa bởi 'retrofit' convertor). –

+0

@VladimirMironov có nó là mã http. Nếu bạn đang trả về một json đơn giản, chỉ với 3 trường như trong câu hỏi của bạn, phân tích cú pháp nó hai lần không phải là một vấn đề. Nếu có một cái gì đó phức tạp hơn có thể bạn sẽ cần một giải pháp tốt hơn – LordRaydenMK

0

1 tùy chỉnh Observable.Operator:

public class YourOperator implements Observable.Operator{ 
    public void onNext(Data data){ 
     if (data.code != 0){ 
      //raise your custom Exception 
     } 
    } 
    public void onError(Throwable e){ 
     //handler Exception 
    } 
} 

2 người sử dụng như:

api.youRequest() 
    .lift(new YourOperator()) 
    .subscribe(....); 
Các vấn đề liên quan