У меня есть класс репозитория, который должен возвращать это: 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);
}
Если кто-то знает лучший подход, пожалуйста, скажите мне.
Спасибо!
Ответ 2 · рейтинг 2 · jkettmann
Я сделал это в проекте, добавив перехватчик при создании остальной службы. Таким образом, ошибки проверяются до того, как запрос достигнет службы отдыха.
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 · m.ostroverkhov
Вы можете попробовать следующее:
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() позволяет воздействовать на элементы ошибок: отправлять соответствующие исключения вниз по течению и передавать уведомления об ошибках как есть.