【问题标题】:Return custom exceptions in case of failure with RxJava + Retrofit在 RxJava + Retrofit 失败的情况下返回自定义异常
【发布时间】:2016-08-06 23:44:46
【问题描述】:

我有一个必须返回的存储库类:Observable<List<SomeObject>, 我这样做:

 @Override
    public Observable<List<SomeObject>> getAllById(Long id) {
        if (!AndroidUtils.isNetworkAvailable(mContext))
            return Observable.error(new NoNetworkConnectionException());

        return mRestService.get(id);
    }

这种方法正常工作,问题是我想返回自定义异常 在失败的情况下,但我不知道使用 rxjava 执行此操作的最佳方法。

到目前为止,唯一可行的解​​决方案是这样的:

@Override
public Observable<List<SomeObject>> getAllById(Long id) {
    if (!AndroidUtils.isNetworkAvailable(mContext))
        return Observable.error(new NoNetworkConnectionException());

    return Observable.create(subscriber -> {
        mRestService.get(id).subscribe(new Observer<List<SomeObject>>() {
            @Override
            public void onCompleted() {
                subscriber.onCompleted();
            }

            @Override
            public void onError(Throwable e) {
                if (e instanceof HttpException  && ((HttpException) e).code() == 401)
                    subscriber.onError(new UnathorizedException());
                else
                    subscriber.onError(e);
            }

            @Override
            public void onNext(List<SomeObject> objects) {
                subscriber.onNext(objects);
            }
        });
    });
}

我知道使用 Observable.create 不是一件好事,但我想不通 另一种方法来做到这一点。

RestService 是这样的:

public interface RestService {

    @GET("objects/{id}")
    Observable<List<SomeObject>> get(@Path("id") Long id);
}

如果有人知道更好的方法,请告诉我。

谢谢!

【问题讨论】:

    标签: android rx-java retrofit2


    【解决方案1】:

    您可以使用运算符onErrorResumeNext 将您的异常映射到另一个异常。

    mRestService.get(id)
                .onErrorResumeNext(e -> {
                    if (e instanceof HttpException  && ((HttpException) e).code() == 401)
                        return Observable.error(new UnathorizedException());
                    else
                        return Observable.error(e);
                })
                .subscribe();
    

    【讨论】:

    • 很抱歉延迟提供反馈。我测试过,效果很好。那么,请求中可能发生的任何异常最终都会调用 onErrorResumeNext() 吗?这个实现有什么缺点,比如使用 Observable.create()?
    • 非常感谢!我只知道这个
    【解决方案2】:

    我在一个项目中通过在创建其余服务时添加拦截器来做到这一点。这样,在请求到达您的休息服务之前检查错误。

    OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
    
    httpClientBuilder.addInterceptor(new ErrorInterceptor());
    
    Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(myBaseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .client(httpClientBuilder.build())
                .build();
    

    ErrorInterceptor 类看起来像

    public class ErrorInterceptor implements Interceptor {
    
        private static final Charset UTF8 = Charset.forName("UTF-8");
    
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request originalRequest = chain.request();
            Response response = chain.proceed(originalRequest);
    
            if (response.code() >= 400) {
                throwError(response);
                return response;
            } else {
                return response;
            }
        }
    
        private void throwError (Response response) throws IOException {
            ResponseBody responseBody = response.body();
            BufferedSource source = responseBody.source();
            source.request(Long.MAX_VALUE); // Buffer the entire body.
            Buffer buffer = source.buffer();
    
            Charset charset = UTF8;
            MediaType contentType = responseBody.contentType();
            if (contentType != null) {
                charset = contentType.charset(UTF8);
            }
    
            if (responseBody.contentLength() != 0) {
                String responseJSON = buffer.clone().readString(charset);
                Gson gson = new Gson();
                Type type = new TypeToken<ErrorResponse>() {}.getType();
                ErrorResponse error = null;
                try {
                    error = gson.fromJson(responseJSON, type);
                }
                catch (Exception e) {
                    int a = 1;
                }
                if (error != null && error.hasErrors())
                    throw ErrorMapper.mapError(error.getFirstError());
            }
        }
    }
    

    还有我的 ErrorResponse 类

    public class ErrorResponse {
    
        private List<Error> errors;
    
        public boolean hasErrors () {
            return errors != null && errors.size() > 0;
        }
    
        public Error getFirstError() {
            if (errors == null || errors.size() == 0) return null;
            return errors.get(0);
        }
    }
    

    在我的 ErrorMapper 中,我只是根据一组来自服务器的可能消息检查错误消息,并创建一个包含要在客户端显示的消息的新错误。

    我只是在这里检查第一个错误,但您应该可以轻松地将其应用于多个错误。

    【讨论】:

      【解决方案3】:

      您可以尝试以下操作:

          RestService restService = Mockito.mock(RestService.class);
      
          Observable<List<Object>> networkExOrEmpty = isNetworkAvailable() ?
                  Observable.empty() :
                  Observable.error(new NoNetworkConnectionException());
      
          Observable<List<Object>> resultOrEx = restService
                  .getAllById(42L)
                  .materialize()
                  .map(res -> {
                      if (res.isOnError()) {
                          Throwable err = res.getThrowable();
                          if (err instanceof HttpException && ((HttpException) err).code() == 401) {
                              return Notification.createOnError(new UnauthrizedException());
                          } else {
                              return Notification.createOnError(err);
                          }
                      } else {
                          return res;
                      }
                  }).dematerialize();
      
      
          Observable<List<Object>> result = networkExOrEmpty.concatWith(resultOrEx);
      

      Observable 开始,它会根据网络连接状态发出错误或什么都不发出,然后将其与Retrofit 服务的结果连接起来。 Observable.materialize() 允许对错误项采取行动:将适当的异常推送到下游,并按原样传递非错误通知。

      【讨论】:

        猜你喜欢
        • 2017-10-26
        • 1970-01-01
        • 1970-01-01
        • 2013-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-04
        • 2016-01-07
        相关资源
        最近更新 更多