I'm getting a list of installed apps on the device. It's a costly operation, so I'm using Rx for that:
Observable<List> observable = Observable.create(subscriber -> { List result = getUserApps(); subscriber.onNext(result); subscriber.onError(new Throwable()); subscriber.onCompleted(); }); observable .map(s -> { ArrayList<String> list = new ArrayList<>(); ArrayList<Application> applist = new ArrayList<>(); for (Application p : (ArrayList<Application>) s) { list.add(p.getAppName()); applist.add(p); } return applist; }) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .doOnError(throwable -> L.e(TAG, "Throwable " + throwable.getMessage())) .subscribe(s -> createListView(s, view));
However, my problem is with handling errors. Normally, user launches this screen, waits for apps to load, selects what is best and goes to next page. However, when user quickly changes the UI - app crashes with NullPointer.
Okay, so I implemented this onError
. However it still doesn't work, and with above usecase it throws me this:
04-15 18:12:42.530 22388-22388/pl.digitalvirgo.safemob 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:52) at android.os.Handler.handleCallback(Handler.java:730) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:176) at android.app.ActivityThread.main(ActivityThread.java:5419) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:525) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862) at dalvik.system.NativeStart.main(Native Method) Caused by: rx.exceptions.OnErrorNotImplementedException at rx.Observable$31.onError(Observable.java:7134) at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:154) at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:111) at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:70) at rx.internal.operators.NotificationLite.accept(NotificationLite.java:147) at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:177) at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.access$000(OperatorObserveOn.java:65) at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:153) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:47) at android.os.Handler.handleCallback(Handler.java:730) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:176) at android.app.ActivityThread.main(ActivityThread.java:5419) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:525) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.Throwable at pl.digitalvirgo.safemob.fragments.wizard.ApplicationsFragment.lambda$getAppList$25(ApplicationsFragment.java:267) at pl.digitalvirgo.safemob.fragments.wizard.ApplicationsFragment.access$lambda$2(ApplicationsFragment.java) at pl.digitalvirgo.safemob.fragments.wizard.ApplicationsFragment$$Lambda$3.call(Unknown Source) at rx.Observable$1.call(Observable.java:145) at rx.Observable$1.call(Observable.java:137) at rx.Observable.unsafeSubscribe(Observable.java:7304) at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:47) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390) at java.util.concurrent.FutureTask.run(FutureTask.java:234) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:153) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:267) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573) at java.lang.Thread.run(Thread.java:841)
How should I properly handle this problem?
Here, in the above code we see as soon as we get an exception in map operator and then we directly goto onError and the onNext doesn't get called or even onComplete. So, to handle the error in cases like this we use different operators and it will not move to onError directly. Let us understand them one by one.
subscribe( item -> { /* handle success */ }, error -> { /* handle failure */ } ); This works best if you throw the exception as early as possible, as then you can do retries, alternative responses etc. easily. Don't exceptions that extend RuntimeException when thrown crash as they are not handled by RxJava?
RxJava is a Java VM implementation of ReactiveX a library for composing asynchronous and event-based programs by using observable sequences. The building blocks of RxJava are Observables and Subscribers. Observable is used for emitting items and Subscriber is used for consuming those items.
onNext(): This method is called when a new item is emitted from the Observable. onError(): This method is called when an error occurs and the emission of data is not successfully completed. onComplete(): This method is called when the Observable has successfully completed emitting all items.
.doOnError()
is an operator, and is not as such a part of the Subscriber
.
Therefore, having a .doOnError()
does not count as an implemented onError()
.
About the question in one of the comments, of course it is possible to use lambdas.
In this case simply replace
.doOnError(throwable -> L.e(TAG, "Throwable " + throwable.getMessage())) .subscribe(s -> createListView(s, view))
with
.subscribe(s -> createListView(s, view), throwable -> L.e(TAG, "Throwable " + throwable.getMessage()))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With