使用RxJava + Retrofit失败时返回自定义异常

时间:2016-08-06 23:44:46

标签: android rx-java retrofit2

我有一个必须返回的存储库类: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);
}

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

谢谢!

3 个答案:

答案 0 :(得分:9)

您可以使用运算符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();

答案 1 :(得分:1)

我在项目中通过在创建休息服务时添加拦截器来完成此操作。这样就可以在请求到达您的休息服务之前检查错误。

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中,我只是根据来自服务器的一组可能消息检查错误消息,并创建一个包含要在客户端上显示的消息的新错误。

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

答案 2 :(得分:0)

您可以尝试以下操作:

    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()允许用户对错误项执行操作:向下游推送相应的异常,并按原样传递非错误通知。