Я пытаюсь обернуть голову вокруг RxJava в настоящее время, но у меня небольшие проблемы с обработкой исключений сервисного вызова в элегантной манере.
В принципе, у меня есть (дооснащение), которая возвращает Observable<ServiceResponse>
. ServiceResponse
определяется следующим образом:
public class ServiceResponse {
private int status;
private String message;
private JsonElement data;
public JsonElement getData() {
return data;
}
public int getStatus() {
return status;
}
public String getMessage() {
return message;
}
}
Теперь я хочу отобразить этот общий ответ на List<Account>
, содержащийся в поле данных JsonElement (я предполагаю, что вам все равно, как выглядит объект Account
, поэтому я не буду загрязнять сообщение с помощью Это). Следующий код работает очень хорошо для случая успеха, но я не могу найти хороший способ обработать мои исключения API:
service.getAccounts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<ServiceResponse, AccountData>() {
@Override
public AccountData call(ServiceResponse serviceResponse) {
// TODO: ick. fix this. there must be a better way...
ResponseTypes responseType = ResponseTypes.from(serviceResponse.getStatus());
switch (responseType) {
case SUCCESS:
Gson gson = new GsonBuilder().create();
return gson.fromJson(serviceResponse.getData(), AccountData.class);
case HOST_UNAVAILABLE:
throw new HostUnavailableException(serviceResponse.getMessage());
case SUSPENDED_USER:
throw new SuspendedUserException(serviceResponse.getMessage());
case SYSTEM_ERROR:
case UNKNOWN:
default:
throw new SystemErrorException(serviceResponse.getMessage());
}
}
})
.map(new Func1<AccountData, List<Account>>() {
@Override
public List<Account> call(AccountData accountData) {
Gson gson = new GsonBuilder().create();
List<Account> res = new ArrayList<Account>();
for (JsonElement account : accountData.getAccounts()) {
res.add(gson.fromJson(account, Account.class));
}
return res;
}
})
.subscribe(accountsRequest);
Есть ли лучший способ сделать это? Эта работает, onError загорится моему наблюдателю, и я получу ошибку, которую я бросил, но это определенно не похоже, что я делаю это правильно.
Спасибо заранее!
Edit:
Позвольте мне уточнить, чего я хочу достичь:
Я хочу иметь класс, который можно вызвать из пользовательского интерфейса (например, Activity или Fragment или что-то еще). Этот класс примет Observer<List<Account>>
как параметр, например:
public Subscription loadAccounts(Observer<List<Account>> observer, boolean forceRefresh) {
...
}
Этот метод вернет подписку, которая может быть отписана при отключении/уничтожении пользовательского интерфейса /etc.
Параметрированный наблюдатель будет обрабатывать onNext для успешных ответов, проходящих в списке учетных записей. OnError будет обрабатывать любые исключения, но также получит любые исключения API (например, если статус ответа!= 200, мы создадим Throwable и передадим его onError). В идеале я не хочу просто "бросать" Исключение, я хочу передать его непосредственно Наблюдателю. Это то, что делают все примеры, которые я вижу.
Усложнение заключается в том, что моя служба Retrofit возвращает объект ServiceResponse
, поэтому мой наблюдатель не может подписаться на это. Самое лучшее, что я придумал, - создать обсервер Observer вокруг моего наблюдателя, например:
@Singleton
public class AccountsDatabase {
private AccountsService service;
private List<Account> accountsCache = null;
private PublishSubject<ServiceResponse> accountsRequest = null;
@Inject
public AccountsDatabase(AccountsService service) {
this.service = service;
}
public Subscription loadAccounts(Observer<List<Account>> observer, boolean forceRefresh) {
ObserverWrapper observerWrapper = new ObserverWrapper(observer);
if (accountsCache != null) {
// We have a cached value. Emit it immediately.
observer.onNext(accountsCache);
}
if (accountsRequest != null) {
// There an in-flight network request for this section already. Join it.
return accountsRequest.subscribe(observerWrapper);
}
if (accountsCache != null && !forceRefresh) {
// We had a cached value and don't want to force a refresh on the data. Just
// return an empty subscription
observer.onCompleted();
return Subscriptions.empty();
}
accountsRequest = PublishSubject.create();
accountsRequest.subscribe(new ObserverWrapper(new EndObserver<List<Account>>() {
@Override
public void onNext(List<Account> accounts) {
accountsCache = accounts;
}
@Override
public void onEnd() {
accountsRequest = null;
}
}));
Subscription subscription = accountsRequest.subscribe(observerWrapper);
service.getAccounts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(accountsRequest);
return subscription;
}
static class ObserverWrapper implements Observer<ServiceResponse> {
private Observer<List<Account>> observer;
public ObserverWrapper(Observer<List<Account>> observer) {
this.observer = observer;
}
@Override
public void onCompleted() {
observer.onCompleted();
}
@Override
public void onError(Throwable e) {
observer.onError(e);
}
@Override
public void onNext(ServiceResponse serviceResponse) {
ResponseTypes responseType = ResponseTypes.from(serviceResponse.getStatus());
switch (responseType) {
case SUCCESS:
Gson gson = new GsonBuilder().create();
AccountData accountData = gson.fromJson(serviceResponse.getData(), AccountData.class);
List<Account> res = new ArrayList<>();
for (JsonElement account : accountData.getAccounts()) {
res.add(gson.fromJson(account, Account.class));
}
observer.onNext(res);
observer.onCompleted();
break;
default:
observer.onError(new ApiException(serviceResponse.getMessage(), responseType));
break;
}
}
}
}
Я все еще чувствую, что я не использую это правильно, хотя. Я определенно не видел, чтобы кто-либо еще использовал ObserverWrapper раньше. Возможно, я не должен использовать RxJava, хотя ребята из SoundCloud и Netflix действительно продали меня на этом в своих презентациях, и я очень хочу его изучить.