问题
It seems to me that RxJava doesn't give good exception tracing.
One example is this which I got from Eclipse when running my unit test. It is not clear which code using the map function.
Is there any special trick to increase the tracing ability? Or is this considered as a bug?
2017-02-28 19:12:44/SGT INFO c.bt.nmdb.ndac.adapter.Bnmpv5Adapter : Current time stamp - Tue Dec 23 23:56:00 SGT 2014
2017-02-28 19:12:45/SGT TRACE c.bt.nmdb.ndac.adapter.Bnmpv5Adapter : @findAndSendDeviceCredentials()
io.reactivex.exceptions.OnErrorNotImplementedException: The mapper function returned a null value.
at io.reactivex.internal.functions.Functions$14.accept(Functions.java:229)
at io.reactivex.internal.functions.Functions$14.accept(Functions.java:226)
at io.reactivex.internal.subscribers.LambdaSubscriber.onError(LambdaSubscriber.java:75)
at io.reactivex.internal.operators.flowable.FlowableDoOnEach$DoOnEachSubscriber.onError(FlowableDoOnEach.java:109)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.onError(BasicFuseableSubscriber.java:100)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.fail(BasicFuseableSubscriber.java:110)
at io.reactivex.internal.operators.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:65)
at io.reactivex.internal.operators.flowable.FlowableFromArray$ArraySubscription.fastPath(FlowableFromArray.java:134)
at io.reactivex.internal.operators.flowable.FlowableFromArray$BaseArraySubscription.request(FlowableFromArray.java:87)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.request(BasicFuseableSubscriber.java:152)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.request(BasicFuseableSubscriber.java:152)
at io.reactivex.internal.subscribers.LambdaSubscriber.request(LambdaSubscriber.java:110)
at io.reactivex.internal.operators.flowable.FlowableInternalHelper$RequestMax.accept(FlowableInternalHelper.java:244)
at io.reactivex.internal.operators.flowable.FlowableInternalHelper$RequestMax.accept(FlowableInternalHelper.java:240)
at io.reactivex.internal.subscribers.LambdaSubscriber.onSubscribe(LambdaSubscriber.java:48)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.onSubscribe(BasicFuseableSubscriber.java:66)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.onSubscribe(BasicFuseableSubscriber.java:66)
at io.reactivex.internal.operators.flowable.FlowableFromArray.subscribeActual(FlowableFromArray.java:37)
at io.reactivex.Flowable.subscribe(Flowable.java:12901)
at io.reactivex.internal.operators.flowable.FlowableMap.subscribeActual(FlowableMap.java:37)
at io.reactivex.Flowable.subscribe(Flowable.java:12901)
at io.reactivex.internal.operators.flowable.FlowableDoOnEach.subscribeActual(FlowableDoOnEach.java:48)
at io.reactivex.Flowable.subscribe(Flowable.java:12901)
at io.reactivex.Flowable.subscribe(Flowable.java:12886)
at io.reactivex.Flowable.subscribe(Flowable.java:12746)
at michsan.adapter.Bnmpv5Adapter_On_FindAndSendDeviceCredentials_TestCase.shouldFindDeviceCredentialsAndBuildResponses(Bnmpv5Adapter_On_FindAndSendDeviceCredentials_TestCase.java:71)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
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.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:63)
... 43 more
Exception in thread "main" io.reactivex.exceptions.OnErrorNotImplementedException: The mapper function returned a null value.
at io.reactivex.internal.functions.Functions$14.accept(Functions.java:229)
at io.reactivex.internal.functions.Functions$14.accept(Functions.java:226)
at io.reactivex.internal.subscribers.LambdaSubscriber.onError(LambdaSubscriber.java:75)
at io.reactivex.internal.operators.flowable.FlowableDoOnEach$DoOnEachSubscriber.onError(FlowableDoOnEach.java:109)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.onError(BasicFuseableSubscriber.java:100)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.fail(BasicFuseableSubscriber.java:110)
at io.reactivex.internal.operators.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:65)
at io.reactivex.internal.operators.flowable.FlowableFromArray$ArraySubscription.fastPath(FlowableFromArray.java:134)
at io.reactivex.internal.operators.flowable.FlowableFromArray$BaseArraySubscription.request(FlowableFromArray.java:87)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.request(BasicFuseableSubscriber.java:152)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.request(BasicFuseableSubscriber.java:152)
at io.reactivex.internal.subscribers.LambdaSubscriber.request(LambdaSubscriber.java:110)
at io.reactivex.internal.operators.flowable.FlowableInternalHelper$RequestMax.accept(FlowableInternalHelper.java:244)
at io.reactivex.internal.operators.flowable.FlowableInternalHelper$RequestMax.accept(FlowableInternalHelper.java:240)
at io.reactivex.internal.subscribers.LambdaSubscriber.onSubscribe(LambdaSubscriber.java:48)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.onSubscribe(BasicFuseableSubscriber.java:66)
at io.reactivex.internal.subscribers.BasicFuseableSubscriber.onSubscribe(BasicFuseableSubscriber.java:66)
at io.reactivex.internal.operators.flowable.FlowableFromArray.subscribeActual(FlowableFromArray.java:37)
at io.reactivex.Flowable.subscribe(Flowable.java:12901)
at io.reactivex.internal.operators.flowable.FlowableMap.subscribeActual(FlowableMap.java:37)
at io.reactivex.Flowable.subscribe(Flowable.java:12901)
at io.reactivex.internal.operators.flowable.FlowableDoOnEach.subscribeActual(FlowableDoOnEach.java:48)
at io.reactivex.Flowable.subscribe(Flowable.java:12901)
at io.reactivex.Flowable.subscribe(Flowable.java:12886)
at io.reactivex.Flowable.subscribe(Flowable.java:12746)
at michsan.adapter.Bnmpv5Adapter_On_FindAndSendDeviceCredentials_TestCase.shouldFindDeviceCredentialsAndBuildResponses(Bnmpv5Adapter_On_FindAndSendDeviceCredentials_TestCase.java:71)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
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.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:63)
... 43 more
回答1:
Building up on akarnokd's answer, you want to use RxJavaPlugins
to add a hook for each Flowable, Observable,... For instance, as akarnokd does in his RxJava2Extensions library with Flowables:
RxJavaPlugins.setOnFlowableAssembly(new Function<Flowable, Flowable>() {
@Override
public Flowable apply(Flowable f) throws Exception {
// Play with the Stack, collect info
return new FlowableOnAssembly(f);
}
});
This way, you can collect info (the stack, for instance) in that FlowableOnAssembly
. At this point, if you can capture crashes, you can already access that info you added (stacktrace, and whatever else you put into FlowableOnAssembly
).
For easier debugging, even in remote devices, you can set a DefaultUncaughtExceptionHandler
to capture any crash and print the info you collected in FlowableOnAssembly
, for instance:
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
if (e instanceOf FlowableOnAssembly) {
// Print the info you captured
}
}
};
This mechanism will be intercepting everything RxJava2 does, so you probably want to put/remove enable/disable it easily. You can go use a small library I wrote for this purpose in:
RxJava2Debug
RxJava2Debug will create a nice simple StackTrace for you. Optionally, it will also filter it so the first entry of the root cause points to your code (this way services like Crashlytics can collate different errors for you).
来源:https://stackoverflow.com/questions/42507511/rxjava-2-doesnt-tell-the-error-line