When I use retrofit with Single if error throw from doAfterSuccess observer onError not call and app crash(UndeliverableException) but when I create Single myself and throw an exception from a doAfterSuccess observer onError call.
Why doAfterSuccess behave two different way?.
The Single protocol is defined as follows: onSubscribe (onSuccess | onError)?. In words, if onSuccess is called, onError can't be called and vice versa. The doAfterSuccess can nowhere to go with respect to this protocol and to avoid losing possibly important exceptions, it is routed to the global error handler. The wiki has more detailed explanations about it.
If you find yourself wanting to invoke onError after an onSuccess, you are probably using the wrong type. The Observable protocol allows calling onError from whitin or after an onNext.
Alternatively, use try-catch in your unreliable onSuccess handler and call the same code as you'd call from onError.
Single.just(1)
.doAfterSuccess(v -> {
try {
process(v);
} catch (Throwable ex) {
reportError(ex);
}
})
.subscribe(v -> { /* ok */ }, e -> { reportError(e); });
Related
I am trying to call multiple network calls(5 in total) ,each is independent of the others. I know how to call network calls in chain with RxJava. But by calling in chain, if there is an error in calling 3rd or 4th network call, the rest of the calls will not be executed. So I want the rest of the network calls to continue to be executed even when the former calls failed. Is there a way to achieve the solution for this situation?
Yes, there is, you can use onErrorResumeNext. example of my code :
primaryMenuFetcher.getMenu()
.observeOn(uiScheduler)
.flatMap { menuItems ->
onView {
primaryMenu = menuItems
setPrimaryMenuList(primaryMenu)
}
return#flatMap model.getPromotions()
}
.onErrorResumeNext { return#onErrorResumeNext model.getPromotions() }
.observeOn(uiScheduler)
.doFinally { onView { hideProgressBar() } }
.subscribe({ fetchedLeagues ->
onView {
featuredLeagues = fetchedLeagues
showPopularLeagues()
setPopularLeaguesList(featuredLeagues)
}
}, {
showError()
})
There is also other Rx2 error handling options. Refer documentation
I think one of the best things here would be mergeDelayError. This will delay the errors until all observables completed or errored.
This would be one option:
Observable.mergeDelayError(
obs1.subscribeOn(Schedulers.io()),
obs2.subscribeOn(Schedulers.io()),
//...
)
.subscribe();
The subscribeOn is more to guarantee that the requests run in parallel. I don't know if the io scheduler is the most suitable in this scenario, but it has been working for me.
If the stream errors, you'll get a composite exception with each problem that occurred for each individual observable. If you need to check these individually you can look at this exception.
I am very new to RxAndroid and am still trying to navigate my way out of the errors that I am making.
Observable.just(RandomComputeManager.getChartData(0,"abcd",new Date()))
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
System.out.println("RXANDROID"+ s.getFood());
Toast.makeText(getActivity(),"HELLO"+s.getFood(),Toast.LENGTH_LONG);
});
I have a heavy computation method here that I am trying to run on RxJava's Schedulers.computation() thread.(I do not know if only calling it in Observable.just is the right way). The method is supposed to throw an exception if it does not have data.
Class RandomComputeManager{
public static getPieChartData(int a,String b,Date c) throws CustomException {
if(haveData){
//All Okay
}
else{
throw new CustomException("No Data");
}
}
The build is failing with error
error: unreported exception CustomException; must be caught or declared to be thrown
I have tried adding an observer to the subscribe method thinking that it has a onError method but neither is that solving this issue nor am I able to fetch my data then due to some ambiguity in the return value of the called method(Don't know if it should be an observable or just the object I need).
Please suggest a way to handle this.
The subscriber function can take another argument of throwable.
Please do like this
Observable.just(RandomComputeManager.getChartData(0,"abcd",new Date()))
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
System.out.println("RXANDROID"+ s.getFood());
Toast.makeText(getActivity(),"HELLO"+s.getFood(),Toast.LENGTH_LONG);
}, throwable ->{
});
Observable.fromCallable(RandomComputeManager.getChartData(0,"abcd",new Date()))
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
System.out.println("RXANDROID"+ s.getFood());
Toast.makeText(getActivity(),"HELLO"+s.getFood(),Toast.LENGTH_LONG);
}, Throwable :: printStackTrace);
This did the trick. Thanks to Gautam Kumar and Raj Suvariya for your help. I found this detail from Exception handling in rxjava
just method accepts parameters that are resolved immediately, so you actually are about to run the computation at the very same line you create your Observable. Also, this is the reason that your exception is not caught, as the getChartData is not called within Observable on-subscribe function.
What you need here is to create the Observable passing a computation function, but you are trying to pass the computed result.
I am used to Kotlin, so sorry if I mess up Java lambdas here, but you should use fromCallable here, like so
Observable.fromCallable(
() -> RandomComputeManager.getChartData(0, "abcd", new Date())))
fromCallable accepts a function that will execute once you subscribe and will emit the function result.
Also, for your purpose it's better to use a Single, as you will have only one item emitted.
Single.fromCallable(
() -> RandomComputeManager.getChartData(0, "abcd", new Date())))
Also if your your CustomException is checked and you don't want to crash, you do have to add onError handling, like already suggested by others.
I have a simple stream like that:
Observable.error<Int>(Exception()).startWith(1).subscribe {
println("Item is $it")
}
Everything is working like expected. First onNext is called with integer 1 and then exception is thrown, however when I change the stream by adding observeOn like that:
Observable.error<Int>(Exception()).startWith(1).observeOn(AndroidSchedulers.mainThread()).subscribe {
println("Item is $it")
}
onNext is never called. Only the exception is thrown. What am I missing here?
From the observeOn document
Note that onError notifications will cut ahead of onNext notifications
on the emission thread if Scheduler is truly asynchronous.
That means when you apply it, the onError is emitted first & hence the onNext is not called as the streams has ended due to onError.
You can do the following in order to receive the onNext first
observeOn(AndroidSchedulers.mainThread(), true)
This tells the Observable to delay the error till the onNext of startWith is passed
On android, I make server calls through retrofit and the server can sometimes return a 500 response.
Is there a reason why onError does not get invoked in the subscriber?
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Response<Void>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
if (isViewAttached()) {
getView().onError(e);
}
}
#Override
public void onNext(Response<Void> response) {
response.code() <-- why would 500 here not get routed to the onError instead?
}
});
This depends part on your configuration and part on how you defined your call, but it will all boil down to one of 2 Observables.
If you look at the source code you can see that if your call returns a type of Response<Foo> Retrofit will internally create either a CallEnqueueObservable<Foo> or CallExecuteObservable for your call. Check it out in the adapt method. For RxJava 1 this is similar, but the observables are called differently. Anyway, internally the way things work are quite the same. The call is executed and onNext is called with a response instance.
If you take a look at how this works inside Retrofit's proxy mechanism, there will always be a response instance even if the response is an Http error. This means that calling onNext will still happen even if the response is an http error itself. You can have a look at the parseReponse method and as you see there's no exception thrown if the status code is 500.
Back to the observables, only when there's an exception will the subscriber's onError be called. Remember, if it's status code 500 there's no exception thrown.
To get your onError to fire for non 2XX http error codes there are different ways, but one possible way is to (if you can afford it) make your call return Observable<Foo> instead of Observable<Response<Foo>>.
This will make retrofit use internally different observables that will make sure to call your subscriber's onError when there's an http error as well as exceptions.
Only network errorsare thrown into onError (e.g. no internet connection).
Think of a 500er as a valid response from the server instead of an error case. Furthermore you want to use the error information the server provided (Body, status code, Headers, etc.). onError can't provide this (unless as an Exception).
AIM: Refresh an adapter when something changes, but only when the user has stopped scrolling the list.
I am trying to throw an error in doOnNext so that I can then wait and retry again. However, this isn't working. I can see that the exception is being thrown, but retryWhen doesn't seem to be doing anything.
What am I doing wrong here?
someObservable()
.observeOn(AndroidSchedulers.mainThread())
.debounce(650, TimeUnit.MILLISECONDS)
.doOnNext(o -> {
if (isListScrolling()) {
throw new IllegalStateException();
}
})
.retryWhen(errors -> errors.flatMap(error -> Observable.timer(100, TimeUnit.MILLISECONDS)))
.subscribe(o -> refreshListNow());
UPDATE:
someObservable is a Subject (Bus) that I'm observing UI events on.
See my comment below, as it seems retry() here won't work how one would expect (i.e. retry does not cause the source to automatically re-emit the last value, unless I was perhaps to use a BehaviorSubject - which isn't suitable here).
You didn't mention what value is emmited. So I'll just make it a string for this example. Checking the scrolling state inside doOnNext is too late, since they can not be propagated back to retryWhen. You can simply use flatMap to throw an error instead.
Observable.just("someValue")
.observeOn(AndroidSchedulers.mainThread())
.debounce(650, TimeUnit.MILLISECONDS)
.flatMap((Func1<String, Observable<?>>) s -> {
if (isListScrolling()) {
// Still scrolling, pass error so we can retry in the next step
return Observable.error(new Exception());
}
// Not scrolling, we can continue
return Observable.just("someValue");
})
.retry()
.subscribe(o -> refreshListNow());