Exception thrown. Add onError handling - android

I've already added onErrorReturn and doOnError, but I still get fatal error. Here's my code:
apiInterface.submitDataToAnalyze("dataToAnalyze", HelperFunctions.authenticationData, 1, uuid, dataToAnalyze.toString(), todayDate)
.onErrorReturn(new Func1<Throwable, BasicResponse>() {
#Override
public BasicResponse call(Throwable throwable) {
Log.e(FILE_NAME, "submitDataToAnalyze throwable??");
Log.e(FILE_NAME, throwable.getMessage().toString());
return null;
}
})
.flatMap(new Func1<BasicResponse, Observable<LocationSuggestion>>() {
#Override
public Observable<LocationSuggestion> call(BasicResponse basicResponse) {
Log.v(FILE_NAME, "basicResponse: " + basicResponse.toString());
if (basicResponse.getResult() == 1) {
//update TABLE_REQUEST_SUGGESTIONS with serverResponse = 1
dbc.updateRequestSuggestionLog(strTodayDate, strDataToAnalyze, basicResponse.getResult());
return apiInterface.getSuggestion("getSuggestion", HelperFunctions.authenticationData, HelperFunctions.requestVersion, uuid,
strLocationLat, strLocationLng, radius, requestForDate);
} else {
return Observable.just(null);
}
}
}).doOnError(new Action1<Throwable>() {
#Override
public void call(Throwable t) {
Log.e(FILE_NAME, "masuk doOnError??");
Log.e(FILE_NAME, t.getMessage().toString());
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe();
}
Specifically, here's the error message:
E/ISRS: masuk doOnError??
E/ISRS: masuk 22??
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.IllegalStateException: Exception thrown on Scheduler.Worker thread. Add `onError` handling.
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:60)
Additional information: the fatal error would occur if submitDataToAnalyze() has problem connecting to my server due to bad connection etc

In onErrorReturn method you return null, and then this null is propagated further along the chain. So it causes NPE (eg basicResponse.toString()). In this case doOnError is invocated, but your subscriber still get Throwable, that stays unhandled.
So you have to pass Subscriber instance with onError implementation.
Another option: in your chain you can use something like onErrorResumeNext(Observable.empty()), so it'll emit nothing and then complete. In this case you are have not to pass Subscriber implementation to subscribe.

Related

RxJava RetryWhen, This processor allows only a single Subscriber

I am learning how to do data polling in RxJava2
Here is my code so far.
private io.reactivex.Single<String> getMyTask() {
return io.reactivex.Single.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
Log.d("ERSEN","Task Started!");
Random random = new Random(System.currentTimeMillis());
if(random.nextBoolean()){
return "WORK COMPLETED";
}
Log.d("ERSEN","Task Had An Error!");
throw new IllegalArgumentException();
}
});
}
The above is my Single which emits a String basically simulating some work.
I also make the task randomly succeed and fail to the test the case when a poll event fails to check if re-subscription occurs correctly
My problem
compositeDisposable.add(getMyTask()
.repeatWhen(new Function<Flowable<Object>, Publisher<?>>() {
#Override
public Publisher<?> apply(final Flowable<Object> objectFlowable) throws Exception {
return objectFlowable.delay(INTERVAL, TimeUnit.SECONDS);
}
})
.retryWhen(throwableFlowable -> throwableFlowable.flatMap(new Function<Throwable, Publisher<?>>() {
#Override
public Publisher<?> apply(Throwable throwable) throws Exception {
if (throwable instanceof ClassCastException) {
return Flowable.error(throwable);
}
return throwableFlowable.delay(INTERVAL, TimeUnit.SECONDS);
}
}))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSuccess, this::onError));
In the above, I am resubscribing to the Observable when it emitted some data successfully.
I am having problems with retryWhen.
For this example I wish to not retry if a ClassCastException occurs.
In my Observable this is not produced which is for a reason because I am testing the logic to retry only on certain errors
However, I am reviving this error with the above code when an error in the Observable is produced
This processor allows only a single Subscriber
I am not sure what is wrong, I have been following this blog post
http://blog.danlew.net/2016/01/25/rxjavas-repeatwhen-and-retrywhen-explained/
Thanks for reading
Let me know if you would like me to post any more details
You are resubscribing to the error flow in your retryWhen which is not allowed and doesn't make sense in your situation. You should delay a value in flatMap instead:
.retryWhen(throwableFlowable -> throwableFlowable.flatMap(
new Function<Throwable, Publisher<?>>() {
#Override
public Publisher<?> apply(Throwable throwable) throws Exception {
if (throwable instanceof ClassCastException) {
return Flowable.error(throwable);
}
return Flowable.just("ignored").delay(INTERVAL, TimeUnit.SECONDS);
}
}
))

Completable creation NPEs only on Android 4

I am getting a lot of NPE crashes on Crashlytics that are related with an rx-wrapper I've created for FirebaseRemoteConfig.
My code:
public Completable fetch() {
remoteConfig.activateFetched();
return Completable.create(e ->
remoteConfig.fetch(cacheExpiration).addOnCompleteListener(
task -> {
if (task.isSuccessful()) {
Log.d(TAG, "Config fetched successfully");
remoteConfig.activateFetched();
if (!e.isDisposed()) {
e.onComplete();
}
} else {
if (!e.isDisposed()) {
Log.e(TAG, "Config fetch error", task.getException());
e.onError(task.getException());
}
}
}));
}
And then I subscribe to it like this:
composite.clear();
composite.add(
helper.fetch()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableCompletableObserver() {
#Override
public void onComplete() {
Timber.tag(TAG).d("Remote config fetched");
}
#Override
public void onError(#NonNull Throwable throwable) {
Timber.tag(TAG).d(throwable, "Remote config fetch error");
}
})
);
I get 3 types of crashes:
Crash #1
Fatal Exception: java.lang.NullPointerException
at io.reactivex.internal.operators.completable.CompletableCreate.subscribeActual(CompletableCreate.java:36)
at io.reactivex.Completable.subscribe(Completable.java:1635)
at io.reactivex.internal.operators.completable.CompletableSubscribeOn$SubscribeOnObserver.run(CompletableSubscribeOn.java:64)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:452)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:61)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:52)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
Crash #2
Fatal Exception: java.lang.NullPointerException
at io.reactivex.internal.operators.completable.CompletableSubscribeOn.subscribeActual(CompletableSubscribeOn.java:36)
at io.reactivex.Completable.subscribe(Completable.java:1635)
at io.reactivex.internal.operators.completable.CompletableObserveOn.subscribeActual(CompletableObserveOn.java:34)
at io.reactivex.Completable.subscribe(Completable.java:
Crash #3
Fatal Exception: java.lang.NullPointerException
at io.reactivex.internal.operators.completable.CompletableObserveOn$ObserveOnCompletableObserver.onSubscribe(CompletableObserveOn.java:68)
at io.reactivex.internal.operators.completable.CompletableSubscribeOn.subscribeActual(CompletableSubscribeOn.java:36)
at io.reactivex.Completable.subscribe(Completable.java:1635)
at io.reactivex.internal.operators.completable.CompletableObserveOn.subscribeActual(CompletableObserveOn.java:34)
at io.reactivex.Completable.subscribe(Completable.java:1635)
The curious thing is that the crashes only happen on Android 4, mainly 4.4 and 4.1.
The thing all these errors have in common is that they appear to be caused by a null where a CompletableObserver is expected.
Given that the subscribeWith gets a valid object the only way for this to occur is through a misconfiguration of the RxJavaPlugin.onSubscribe hook, which can wrap observers and thus for some reason replace the observer with a null.
I suggest inspecting the onCompletableSubscribe field in the RxJavaPlugins class using reflection to see if there is some callback that might be acting weirdly on those java versions.

Retrofit2 + RxJava error handling

I am using RxJava and Retrofit2 (with OkHttp as the HTTP client) to do networking and am trying to understand how different errors are handled by Retrofit2 and how they look from the RxJava side. The following code illustrates an RxJava Subscriber callback for a network call (made with Retrofit).
Subscription subscription = observable
.subscribeOn(mScheduler)
.observeOn(mAndroidScheduler)
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() {
Timber.d("onCompleted called");
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
#Override
public void onError(Throwable e) {
Timber.d("onError called");
Timber.d(e.toString());
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
#Override
public void onNext(User user) {
Timber.d("onNext called");
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
mActivityView.launchMainActivity();
}
});
My question is, in what cases will onError() be called and once it's been called, how can I interrogate the Throwable to determine the cause?
From the Retrofit source it looks like the only Throwables that are possible to see are IOException and HttpException. Can anyone verify that that is true?
Here's the basics: onError() will be called if:
the observable you're subscribing to throws an exception (e.g. you get an IOException while trying to read a file)
an exception is raised in your onNext() method.
If there's an exception in your onComplete(), RxJava will propagate an rx.exceptions.OnCompletedFailedException and if there's an exception in onError() - you'll get rx.exceptions.OnErrorFailedException.
That said, you can just probe the Throwable you receive in your onError() method for exceptions that you're expecting. For example you know that if your API call results in client error (4xx), Retrofit will wrap it into HttpException. If there's a timeout with the request you'll get a SocketTimeoutException. Here's a rough example:
#Override
public void onError(Throwable e) {
Timber.d("onError called");
Timber.d(e.toString());
handleError(e);
}
private handleError(Throwable throwable) {
if (throwable instanceof HttpException) {
HttpException httpException = (HttpException)throwable;
int statusCode = httpException.code();
// handle different HTTP error codes here (4xx)
} else if (throwable instanceof SocketTimeoutException) {
// handle timeout from Retrofit
} else if (throwable instanceof IOException) {
// file was not found, do something
} else {
// generic error handling
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
Do not use onError for flow. That'd be as bad as try-catch for flow.
Error HTTP codes, are valid responses and you should not deal with them in onError.
You can wrap the return type of your Retrofit services in Result, that gives you the means to get information about what happen with your call without throwing exceptions.
You can handle the state of your app using this pattern:
service.getSomething()
.map(r -> Model.success(r.response()))
.onErrorReturn(Model::error)
.observeOn(AndroidSchedulers.mainThread())
.startWith(Resource.loading())
.subscribe(r -> {
myProgressBar.setVisible(r.isLoading());
if (r.isSuccess()) {
handleSuccess(); // e.g. 400 is also success but needs handling
}
if (r.isError()) {
handleError();
}
}, OnErrorNotImplementedException::new);
See how I tried to handle all possible states within the stream and deliberately I throw OnErrorNotImplementedException for something I might've missed. This is very personal but I prefer to crash-fast-and-furious rather than being in an unknown state for a while that later will manifest in a crash harder to debug.
In Kotlin I have used bellow like..
disposable.add(apiService.getLogin_service(parment1,parment1)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<Login_Reg_Data_Model>() {
override fun onSuccess(model: Login_Reg_Data_Model) {
//success
}
override fun onError(e: Throwable) {
if (e is HttpException) {
// We had non-200 http error
Log.e("time exceptionr******>",e.message)
} else if (e is SocketTimeoutException) {
//time exception
Log.e("time exception******>",e.message)
} else if (e is IOException) {
// A network error
Log.e("network error******>",e.message)
} else {
//unknown error
Log.e("unknown error******>",e.message)
}
}
})
)

Prevent OnErrorNotImplementedException

I want to achieve that if i call the Obervable.subscribe(Action1) method, it does not throw OnErrorNotImplementedException anywhere, but if i call Obervable.subscribe(Action1, Action1), the second action is called when an error is raised as normal. I tried two ways:
.onErrorResumeNext(Observable.empty())
This way OnErrorNotImplementedException is not thrown, however if i pass also the second action, the action is never called either.
Second:
.lift(new Observable.Operator<T, T>() {
#Override
public Subscriber<? super T> call(Subscriber<? super T> subscriber) {
return new Subscriber<T>() {
#Override
public void onCompleted() {
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
}
#Override
public void onError(Throwable e) {
if (!subscriber.isUnsubscribed()) {
try {
subscriber.onError(e);
} catch (Throwable t) {
if (!(t instanceof OnErrorNotImplementedException)) {
throw t;
}
}
}
}
#Override
public void onNext(T t) {
if (!isUnsubscribed()) {
subscriber.onNext(t);
}
}
};
}
});
The problem with this if observeOn() is called later then this will be asynchronous and obviously my exception handling here will not work.
Is there way to achieve this. I wish there would be a subscribe() method which does not throw OnErrorNotImplementedException in onError.
Here is another possible solution, you can define the onNext and a Throwable (also you cannot loose the lambda syntax):
.subscribe(t -> doSomething(t), e -> showError(e));
here's how we do it at work. Instead of making actions we made an abstract NYTSubscriber which has onError and onCompleted implemented. This way you can use this subscriber and only implement the onNext callback OR you can override onError and onCompleted when necessary
public abstract class NYTSubscriber<T> extends Subscriber<T> {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
}
If you are using Kotlin then the syntax is like
.subscribe({success -> doOnSuccess(success)},{error -> doOnError(error)})
When you do like this '.onErrorResumeNext(Observable.empty())' your stream will be completed when error occurs - that's why your actions is never called.
You can user '.retry()' and your stream will be restarted automatically when you have an error.
This Error comes when calling the Subscribe method, but not providing onError() callbacks.
Kotlin
subscribe(object : DisposableCompletableObserver() {
override fun onComplete() {
// on successful completion }
override fun onError(e: Throwable) {
//error message
}
}
)

How to subscribe to click events so exceptions don't unsubscribe?

I'm using RxJava for Android (RxAndroid) and I subscribe to click events of a view, and do something on them as follows:
subscription = ViewObservable.clicks(view, false)
.map(...)
.subscribe(subscriberA);
The problem is whenever there is an exception, subscriberA automatically unsubscribes, leading to the next click not triggering anything.
How to handle exceptions so to intercept all the click events regardless of exceptions being thrown?
Use retry method:
subscription = ViewObservable.clicks(view, false)
.map(...)
.retry()
.subscribe(subscriberA)
However, you will not receive any exception in onError.
To handle exceptions with retry (resubscribe) logic use retryWhen:
subscription = ViewObservable.clicks(view, false)
.map(...)
.retryWhen(new Func1<Observable<? extends Notification<?>>, Observable<?>>() {
#Override
public Observable<?> call(Notification errorNotification) {
Throwable throwable = errorNotification.getThrowable();
if (errorNotification.isOnError() && handleError(throwable)) {
// return the same observable to resubscribe
return Observable.just(errorNotification);
}
// return unhandled error to handle it in onError and unsubscribe
return Observable.error(throwable);
}
private boolean handleError(Throwable throwable) {
// handle your errors
// return true if error handled to retry, false otherwise
return true;
}
}
.subscribe(subscriberA)

Categories

Resources