UndeliverableException while calling onError of ObservableEmitter in RXjava2
The problem is like you say you need to check if Subscriber
is already disposed, that's because RxJava2 is more strict regarding errors that been thrown after Subscriber
already disposed.
RxJava2 deliver this kind of error to RxJavaPlugins.onError
that by default print to stack trace and calls to thread uncaught exception handler. you can read full explanation here.
Now what's happens here, is that you probably unsubscribed (dispose) from this Observable
before query was done and error delivered and as such - you get the UndeliverableException
.
I wonder how can call onError coz i need to notify my UI level.
as this is happened after your UI been unsubscribed the UI shouldn't care. in normal flow this error should delivered properly.
Some general points regarding your implementation:
- the same issue will happen at the
onError
in case you've been unsubscribed before. - there is no cancellation logic here (that's what causing this problem) so request continue even if
Subscriber
unsubscribed. - even if you'll implement this logic (using
ObservableEmitter.setCancellable()
/setDisposable()
) you will still encounter this problem in case you will unsubscribe before request is done - this will cause cancellation and youronFailure
logic will callonError()
and the same issue will happen. - as you performing an async call via Retrofit the specified subscription
Scheduler
will not make the actual request happen on theScheduler
thread but just the subscription. you can useObservable.fromCallable
and Retrofit blocking callexecute
to gain more control over the actual thread call is happened.
to sum it up -
guarding calls to onError()
with ObservableEmitter.isDiposed()
is a good practice in this case.
But I think the best practice is to use Retrofit RxJava call adapter, so you'll get wrapped Observable
that doing the Retrofit call and already have all this considerations.
Since version 2.1.1 tryOnError
is available:
The emitter API (such as FlowableEmitter, SingleEmitter, etc.) now features a new method, tryOnError that tries to emit the Throwable if the sequence is not cancelled/disposed. Unlike the regular onError, if the downstream is no longer willing to accept events, the method returns false and doesn't signal an UndeliverableException.
https://github.com/ReactiveX/RxJava/blob/2.x/CHANGES.md
I found out that this issue was caused by using incorrect context when retrieving view model in Fragment:
ViewModelProviders.of(requireActivity(), myViewModelFactory).get(MyViewModel.class);
Because of this, the view model lived in context of activity instead of fragment. Changing it to following code fixed the problem.
ViewModelProviders.of(this, myViewModelFactory).get(MyViewModel.class);