RxJava 사용하다보면 에러는 났는데, 어디서 에러 났는지 모를 때가 있습니다. 보통은 항상 onError를 추가하지만, 그래도 가끔은 안 잡힐 때가 있습니다. 그럴 때 유용합니다.
1. 기본 코드
Observable.timer(5, TimeUnit.SECONDS) .map { it -> null } .subscribe( { Log.i("APP#", it) } )
결과: 앱 강제 종료됨. 어느 클래스에서 발생했는지 알 수 없음.
로그:
W/System.err: io.reactivex.exceptions.OnErrorNotImplementedException: The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | java.lang.NullPointerException: The mapper function returned a null value. W/System.err: at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:704) W/System.err: at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:701) W/System.err: at io.reactivex.internal.observers.LambdaObserver.onError(LambdaObserver.java:77) W/System.err: at io.reactivex.internal.observers.BasicFuseableObserver.onError(BasicFuseableObserver.java:100) W/System.err: at io.reactivex.internal.observers.BasicFuseableObserver.fail(BasicFuseableObserver.java:110) W/System.err: at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:59) W/System.err: at io.reactivex.internal.operators.observable.ObservableTimer$TimerObserver.run(ObservableTimer.java:67) W/System.err: at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:38) W/System.err: at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:26) W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:266) W/System.err: at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) W/System.err: at java.lang.Thread.run(Thread.java:764) W/System.err: Caused by: java.lang.NullPointerException: The mapper function returned a null value. W/System.err: at io.reactivex.internal.functions.ObjectHelper.requireNonNull(ObjectHelper.java:39) W/System.err: at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:57) W/System.err: ... 8 more
2. onError 추가 시
Observable.timer(5, TimeUnit.SECONDS) .map { it -> null } .subscribe( { Log.i("APP#", it) }, { Log.w("APP#", it) } )
결과: 앱 종료 안됨. 어느 클래스에서 발생했는지 알 수 없음.
로그:
W/APP#: java.lang.NullPointerException: The mapper function returned a null value. at io.reactivex.internal.functions.ObjectHelper.requireNonNull(ObjectHelper.java:39) at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:57) at io.reactivex.internal.operators.observable.ObservableTimer$TimerObserver.run(ObservableTimer.java:67) at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:38) at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:26) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) at java.lang.Thread.run(Thread.java:764)
3. RxJavaPlugins.setErrorHandler 추가 시
// application 클래스 RxJavaPlugins.setErrorHandler { Log.w("APP#", it) }
Observable.timer(5, TimeUnit.SECONDS) .map { it -> null } .subscribe( { Log.i("APP#", it) } )
결과: 앱 강제 종료 안됨. 어느 클래스에서 발생했는지 알 수 없음.
로그:
W/APP#: io.reactivex.exceptions.OnErrorNotImplementedException: The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | java.lang.NullPointerException: The mapper function returned a null value. at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:704) at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:701) at io.reactivex.internal.observers.LambdaObserver.onError(LambdaObserver.java:77) at io.reactivex.internal.observers.BasicFuseableObserver.onError(BasicFuseableObserver.java:100) at io.reactivex.internal.observers.BasicFuseableObserver.fail(BasicFuseableObserver.java:110) at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:59) at io.reactivex.internal.operators.observable.ObservableTimer$TimerObserver.run(ObservableTimer.java:67) at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:38) at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:26) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) at java.lang.Thread.run(Thread.java:764) Caused by: java.lang.NullPointerException: The mapper function returned a null value. at io.reactivex.internal.functions.ObjectHelper.requireNonNull(ObjectHelper.java:39) at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:57) at io.reactivex.internal.operators.observable.ObservableTimer$TimerObserver.run(ObservableTimer.java:67) at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:38) at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:26) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) at java.lang.Thread.run(Thread.java:764)
4. RxDogTag도 추가 시
// application 클래스 RxDogTag.install() RxJavaPlugins.setErrorHandler { Log.w("APP#", it) }
Observable.timer(5, TimeUnit.SECONDS) .map { it -> null } .subscribe( { Log.i("APP#", it) } )
결과: 앱 강제 종료 안됨. 어느 클래스에서 발생했는지 알 수 있음.
로그:
W/APP#: io.reactivex.exceptions.OnErrorNotImplementedException: The mapper function returned a null value. Caused by: java.lang.NullPointerException: The mapper function returned a null value. at kr.susemi99.testrxdogtag.MainActivity.test2(MainActivity.kt:29) at [[ ↑↑ Inferred subscribe point ↑↑ ]].(:0) at [[ ↓↓ Original trace ↓↓ ]].(:0) at io.reactivex.internal.functions.ObjectHelper.requireNonNull(ObjectHelper.java:39) at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:57) at io.reactivex.internal.operators.observable.ObservableTimer$TimerObserver.run(ObservableTimer.java:67) at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:38) at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:26) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) at java.lang.Thread.run(Thread.java:764)
결론: RxDogTag( https://github.com/uber/RxDogTag )와 RxJavaPlugns.setErrorHandler를 같이 사용하세요~