RxDogTag - 获取Observer的onError()栈迹的通用工具

299 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

RxDogTag 是一个通用程序, 用于标记RxJava 2+ Observer的初始订阅点. 它的目的在于显示订阅位置用于报告/调查未处理的错误. 但它仅用于未实现onError的RxJava Observers.

安装

在应用的生命周期早期通过RxDogTag.install()进行安装. 对于Android的话, 推荐位置在于Application#onCreate().

安装时如果想要更多的配置, 可通过:

RxDogTag.builder().configureWith(AutoDisposeConfigurer::configure).install();

RxDogTag在安装时会在RxJavaPlugins中以覆盖的方式安装很多必要的hook. 包括:

  • RxJavaPlugins#setOnFlowableSubscribe(BiFunction)
  • RxJavaPlugins#setOnObservableSubscribe(BiFunction)
  • RxJavaPlugins#setOnMaybeSubscribe(BiFunction)
  • RxJavaPlugins#setOnSingleSubscribe(BiFunction)
  • RxJavaPlugins#setOnCompletableSubscribe(BiFunction)

这意味着如果之前已经安装了相关的hook, RxDogTag会将之前的相同的hook覆盖掉.

举例来说, Observable.just(1).subscribeOn(Schedulers.IO).map(i -> null).subscribe(s -> println(s)).

在使用RxDogTag之前, 相关的栈迹为:

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 | 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.ObservableSubscribeOn$SubscribeOnObserver.onNext(ObservableSubscribeOn.java:58) at io.reactivex.internal.operators.observable.ObservableScalarXMap$ScalarDisposable.run(ObservableScalarXMap.java:248) at io.reactivex.internal.operators.observable.ObservableJust.subscribeActual(ObservableJust.java:35) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96) at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:578) at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66) at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) 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) ... 14 more

这几乎不太可能看得出来哪里出了问题.

而在启用了RxDogTag之后, 栈迹则变成了:

io.reactivex.exceptions.OnErrorNotImplementedException: The mapper function returned a null value. Caused by: java.lang.NullPointerException: The mapper function returned a null value. at anotherpackage.ReadMeExample.complex(ReadMeExample.java:55) 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.ObservableSubscribeOn$SubscribeOnObserver.onNext(ObservableSubscribeOn.java:58) at io.reactivex.internal.operators.observable.ObservableScalarXMap$ScalarDisposable.run(ObservableScalarXMap.java:248) at io.reactivex.internal.operators.observable.ObservableJust.subscribeActual(ObservableJust.java:35) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96) at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:578) at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66) at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)

anotherpackage.ReadMeExample.complex(ReadMeExample.java:55)能轻而易举地看出来错误发生在哪里.

从这里可以看出来RxDogTag的意义所在.

配置

候选API RxDogTag.builder()能够添加配置, 诸如注解控制, 栈迹元素定位等等.

Custom handlers

如果自定义观察者装饰了其它类型观察者的话, 这个装饰信息可以通过接口ObserverHandler传递给RxDogTag. 这个接口用于揭开自定义观察者的代理观察者及其潜在的行为. 该接口可通过RxDogTag.Builder#addObserverHandlers(...)来进行配置.

Ignored packages

RxDogTag在检查堆栈来推断订阅点时需要忽略某些包(如RxDogTag自己的或者RxJava的)以提升效率. 这些包可以通过RxDogTag.Builder#addIgnoredPackages(...)来添加.

AutoDispose support

AutoDispose是一个自动处理流的库, 内部通过自己的装饰器观察者来实现. AutoDispose可以通过其在AutoDisposingObserver#delegateObserver()RxDogTag一起工作。通过单独的rxdogtag-autodispose依赖及其AutoDisposeObserverHandler单例可以获得对此的支持.

背后的原理

RxDogTag is built on top of LambdaConsumerIntrospection, which could be found in its implementation PR.

它的决定树是这样的:

-> Is the observer an instance of `LambdaConsumerIntrospection`? 
    -> Does `hasCustomOnError()` return `false`? 
        -> Decorate with a DogTag observer.

Proguard/R8

要准确标识subscribe()所在行, RxDogTag要求保留io.reactivexcom.uber.rxdogtag包中的类型名. 尽管这并不会阻止压缩无用类型, 但确实会对API大小有负责影响. 所以对于对此敏感的开发者, RxDogTag可能并不适用于你.

RxDogTag在jar资源中自带rxdogtag.pro规则以自动处理混淆问题.