में एपीआई अपवादों को संभालना मैं वर्तमान में आरएक्सजेवा के आसपास अपने सिर को लपेटने की कोशिश कर रहा हूं, लेकिन मुझे एक सुंदर तरीके से सेवा कॉल अपवादों को संभालने में थोड़ी परेशानी हो रही है।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
वस्तु कैसा दिखता मैप करने के लिए है, इसलिए मैं जीता इसके साथ पोस्ट प्रदूषित नहीं करें)। निम्नलिखित कोड सफलता मामले के लिए वास्तव में अच्छी तरह से काम करता है, लेकिन मैं अपने एपीआई अपवाद को संभालने के लिए एक अच्छा तरीका नहीं मिल सकता है:
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);
वहाँ यह करने के लिए एक बेहतर तरीका है? यह काम करता है, ऑनर मेरे पर्यवेक्षक को आग लगाएगा, और मुझे जो त्रुटि आई है, उसे प्राप्त होगा, लेकिन यह निश्चित रूप से ऐसा नहीं लगता है कि मैं यह सही कर रहा हूं।
अग्रिम धन्यवाद!
संपादित करें:
मुझे स्पष्ट मैं वास्तव में क्या हासिल करना चाहते हैं करते हैं:
मैं एक वर्ग है कि यूआई से कहा जा सकता है करना चाहते हैं (उदाहरण के लिए एक गतिविधि, या टुकड़ा, या जो कुछ भी) । यही कारण है कि वर्ग तो जैसे एक पैरामीटर के रूप में एक Observer<List<Account>>
ले जाएगा:
public Subscription loadAccounts(Observer<List<Account>> observer, boolean forceRefresh) {
...
}
कि विधि एक सदस्यता कि सदस्यता रद्द कर दी हो सकता है जब यूआई/अलग है को नष्ट कर दिया/आदि लौट आते हैं।
पैरामीटरयुक्त पर्यवेक्षक खाते की सूची में उत्तीर्ण सफल प्रतिक्रियाओं के लिए अगला पर संभाल लेंगे। ऑनरर किसी भी अपवाद को संभालेगा, लेकिन किसी भी एपीआई अपवादों को भी पारित करेगा (उदाहरण के लिए यदि प्रतिक्रिया स्थिति! = 200 हम एक थ्रोबल बनाएंगे और इसे एरर पर पास करेंगे)। आदर्श रूप में मैं अपवाद को "फेंकना" नहीं चाहता हूं, मैं इसे सीधे पर्यवेक्षक को पास करना चाहता हूं। यही वह उदाहरण है जो मैं देखता हूं।
जटिलता यह है कि मेरी रेट्रोफिट सेवा ServiceResponse
ऑब्जेक्ट देता है, इसलिए मेरा पर्यवेक्षक उस पर सब्सक्राइब नहीं कर सकता है। सबसे अच्छा मैं ले कर आए हैं तो जैसे मेरी पर्यवेक्षक के चारों ओर एक ऑब्जर्वर आवरण बनाने के लिए, यह है:
@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;
}
}
}
}
मैं अभी भी है कि मैं यह सही है, हालांकि उपयोग नहीं कर रहा है। मैंने निश्चित रूप से पहले किसी ऑब्जर्वरवापर का उपयोग करके किसी और को नहीं देखा है। शायद मुझे आरएक्सजेवा का उपयोग नहीं करना चाहिए, हालांकि साउंडक्लाउड और नेटफ्लिक्स के लोगों ने वास्तव में मुझे अपनी प्रस्तुतियों में बेच दिया और मैं इसे सीखने के लिए उत्सुक हूं।
धन्यवाद त्रुटि के बारे में यह अच्छा लेख पढ़ सकते हैं, इस कोड को निश्चित रूप से अधिक पठनीय है :) –
यह सिर्फ मुझे है, या नहीं एक Action1 कॉल से एक अपवाद फेंकने के लिए संभव है। यहां आपके उदाहरण में आप केवल नए कॉल कर रहे हैं लेकिन फेंक नहीं सकते हैं। तो क्या यह सिर्फ मंजिल पर नहीं छोड़ा जाएगा और आगे के बजाय अग्रेषित किया जाएगा? – schwiz
@schwiz एक क्रिया के अंदर से अपवाद फेंकना संभव है। मैंने स्पष्टता के लिए लापता फेंक स्टेटमेंट जोड़ा है। लेकिन ऐसा कहा जा रहा है कि मैंने अपनी पोस्ट में एक संपादन जोड़ा है क्योंकि मैं अब इस दृष्टिकोण का उपयोग नहीं करता हूं। मैं flatMap का उपयोग करें। –