Tôi đang cố gắng bọc đầu xung quanh RxJava, nhưng tôi gặp một chút rắc rối khi xử lý các ngoại lệ gọi dịch vụ một cách thanh lịch.Xử lý các ngoại lệ API trong RxJava
Về cơ bản, tôi có dịch vụ (trang bị thêm) trả lại số Observable<ServiceResponse>
. ServiceResponse
được định nghĩa như sau:
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;
}
}
Bây giờ những gì tôi muốn là để lập bản đồ rằng phản ứng tổng quát để một List<Account>
chứa trong các lĩnh vực dữ liệu JsonElement (tôi giả sử bạn không quan tâm những gì các đối tượng Account
trông như thế nào, vì vậy tôi đã thắng 'gây ô nhiễm bài đăng với nó). Mã sau hoạt động thực sự tốt cho trường hợp thành công, nhưng tôi không thể tìm thấy cách tốt để xử lý các ngoại lệ API của mình:
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);
Có cách nào tốt hơn không? không hoạt động, lỗi này sẽ kích hoạt người quan sát của tôi và tôi sẽ nhận được lỗi mà tôi đã ném, nhưng chắc chắn dường như tôi không làm điều này đúng.
Cảm ơn trước!
Edit:
Hãy để tôi làm rõ chính xác những gì tôi muốn đạt được:
Tôi muốn có một lớp học mà có thể được gọi từ UI (ví dụ như một hoạt động, hoặc Fragment, hoặc bất cứ điều gì) . lớp đó sẽ mất một Observer<List<Account>>
như một tham số như sau:
public Subscription loadAccounts(Observer<List<Account>> observer, boolean forceRefresh) {
...
}
rằng phương pháp sẽ trả về một thuê bao thể huỷ đăng ký khi giao diện người dùng được tách/phá hủy/etc.
Trình quan sát được tham số hóa sẽ xử lý onNext cho các câu trả lời thành công trong danh sách Tài khoản. OnError sẽ xử lý bất kỳ ngoại lệ nào, nhưng cũng sẽ vượt qua bất kỳ ngoại lệ API nào (ví dụ: nếu trạng thái phản hồi! = 200, chúng tôi sẽ tạo Throwable và chuyển nó lên onError). Lý tưởng nhất là tôi không muốn chỉ "ném" ngoại lệ, tôi muốn chuyển nó trực tiếp cho người quan sát. Đó là tất cả những gì tôi thấy.
Biến chứng là dịch vụ Retrofit của tôi trả về đối tượng ServiceResponse
, vì vậy người quan sát của tôi không thể đăng ký điều đó. Điều tốt nhất tôi đã đi lên với là tạo ra một wrapper Observer xung quanh Observer của tôi, như vậy:
@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's 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;
}
}
}
}
tôi vẫn cảm thấy như tôi không sử dụng này một cách chính xác mặc dù. Tôi chắc chắn đã không nhìn thấy bất cứ ai khác bằng cách sử dụng ObserverWrapper trước đây. Có lẽ tôi không nên sử dụng RxJava, mặc dù những người ở SoundCloud và Netflix thực sự đã bán tôi trên đó trong bài thuyết trình của họ và tôi rất háo hức muốn học nó.
Nhờ lỗi, mã này chắc chắn là dễ đọc hơn :) –
là nó chỉ cho tôi, hoặc là nó không thể ném một ngoại lệ từ một cuộc gọi Action1. Ở đây trong ví dụ của bạn, bạn chỉ cần gọi mới nhưng không ném. Vì vậy, nó sẽ không chỉ có được giảm xuống sàn và onNext được gọi thay vì onError? – schwiz
@schwiz nó có thể ném ngoại lệ từ bên trong một hành động. Tôi đã thêm tuyên bố ném thiếu cho rõ ràng. Nhưng với điều đó đang được nói tôi đã thêm một chỉnh sửa vào bài viết của tôi vì tôi không sử dụng phương pháp này nữa. Tôi sử dụng flatMap. –