RxJava2中的标准错误处理程序,或者即使实现了onError,RxJava为何导致应用程序崩溃的原因

本文将讨论RxJava2 2.0.6版和更高版本中的UndeliverableException 。 如果有人相撞而无法解决,或者根本听不到这个问题-我要一只猫。 从RxJava1过渡到RxJava1后,他们促使翻译production的问题。 原文写于2017年12月28日,但总比没有发现要晚。
我们都是优秀的开发人员,使用RxJava时会在onError捕获错误。 这意味着我们已经保护自己免受应用程序崩溃的侵害,对吗?

不,不是真的。

下面我们将看几个示例,其中即使正确实现了onError ,应用程序也会由于RxJavaRxJava

RxJava基本错误处理RxJava


RxJava使用RxJavaPlugins.onError作为基本错误处理程序。 它处理所有无法传递给订户的错误。 默认情况下,所有错误都会发送给它,因此可能会导致严重的应用程序崩溃。
2.0.6描述了此行为:
2.x设计的目标之一是没有丢失的错误。 有时序列在源引发onError之前结束或被取消。 在这种情况下,错误无处可去,它被发送到RxJavaPlugins.onError

如果RxJava没有基本的错误处理程序,则此类错误将对我们隐藏,开发人员将对代码中的潜在问题一无所知。

从版本2.0.6开始, RxJavaPlugins.onError聪明,并共享库/实现错误以及无法传递错误的情况。 分配给“错误”类别的错误按原样调用,其余错误包装在UndeliverableException ,然后调用。 您可以在这里看到所有这些逻辑( onErrorisBug )。

新手在RxJava的主要错误之一是OnErrorNotImplementedException 。 如果observable导致错误,并且订阅者中未实现onError方法,则会发生此错误。 该错误是一个错误的示例,对于基本错误处理程序RxJava是一个“错误”,并且不会变成UndeliverableException

UndeliverableException


由于与“错误”相关的错误易于修复,因此我们不再赘述。 RxJava封装在UndeliverableException中的错误更为有趣,因为并非总是很明显为什么无法将错误传递给onError

发生这种情况的情况取决于来源和订户的具体操作。 我们将在下面考虑示例,但通常可以说,如果没有活动的订户可以将错误传递给用户,则会发生这种错误。

zipWith()的示例


可以引发UndeliverableException的第一个选项是zipWith

 val observable1 = Observable.error<Int>(Exception()) val observable2 = Observable.error<Int>(Exception()) val zipper = BiFunction<Int, Int, String> { one, two -> "$one - $two" } observable1.zipWith(observable2, zipper) .subscribe( { System.out.println(it) }, { it.printStackTrace() } ) 

我们将两个来源合并在一起,每个来源都会导致错误。 我们期望什么? 我们可以假设onError将被调用两次,但这与Reactive streams 规范相矛盾。
在单次调用终端事件( onErroronCompelete )之后,要求不再进行任何调用

事实证明,通过单次调用onError不再可以onError第二次调用。 当源中发生第二个错误时会发生什么? 它将被传递到RxJavaPlugins.onError

进入这种情况的一种简单方法是使用zip组合网络调用(例如,两个Retrofit调用返回Observable )。 如果两个调用都发生错误(例如,没有Internet连接),则两个源都将导致错误,第一个将属于onError实现,第二个将传递给基本错误处理程序( RxJavaPlugins.onError )。

没有订阅者的可连接示例


ConnectableObservable也可以引发UndeliverableException 。 值得回顾的是,无论活动订户的存在与否, ConnectableObservable都会引发事件,只需调用connect()方法即可。 如果在没有订阅者的情况下ConnectableObservable发生错误,它将被传递给基本错误处理程序。

这是一个很纯真的例子,可能会导致这样的错误:

 someApi.retrofitCall() //     Retrofit .publish() .connect() 

如果someApi.retrofitCall()导致错误(例如,没有Internet连接),则应用程序将崩溃,因为网络错误将传递给RxJava基本错误RxJava

这个示例似乎是虚构的,但是很容易陷入ConnectableObservable仍处于连接状态但没有订阅者的情况。 我在调用API时使用autoConnect()时遇到了这个问题。 autoConnect()不会自动禁用Observable 。 我取消了ActivityonStop方法的订阅,但是在Activity销毁后应用程序因UndeliverableException而返回了网络调用的结果。

错误处理


那么如何处理这些错误呢?

第一步是查看发生的错误,并尝试确定导致这些错误的原因。 理想情况下,如果可以从RxJavaPlugins.onError解决问题,以防止将该错误RxJavaPlugins.onErrorRxJavaPlugins.onError

zipWith示例的解决方案是采用一个或两个源,并实现其中一种捕获错误的方法 。 例如,您可以使用onErrorReturn传递默认值而不是错误。

ConnectableObservable示例更易于修复-只要确保在最后一个订阅者退订时断开Observable的连接即可。 例如, autoConnect()有一个重载的实现,该实现带有捕获连接时间的函数( 可以在此处查看更多信息 )。

解决问题的另一种方法是用您自己的替换基本错误处理程序。 RxJavaPlugins.setErrorHandler(Consumer<Throwable>)方法将帮助您。 如果这是适合您的解决方案,则可以捕获所有发送到RxJavaPlugins.onError的错误,并根据需要进行处理。 该解决方案可能非常复杂-请记住, RxJavaPlugins.onError会从应用程序中的所有RxJava流接收错误。

如果您手动创建Observable ,则可以调用emitter.tryOnError()而不是emitter.tryOnError() 。 仅当流没有终止并且具有订阅者时,此方法才会报告错误。 请记住,此方法是实验性的。

本文的主旨是,如果仅在订阅服务器中实现onError ,则无法确定使用RxJava时没有错误。 您应该注意订户可能无法发现错误的情况,并确保已处理这些情况。

Source: https://habr.com/ru/post/zh-CN422611/


All Articles