本文将讨论
RxJava2 2.0.6版和更高版本中的
UndeliverableException 。 如果有人相撞而无法解决,或者根本听不到这个问题-我要一只猫。 从
RxJava1过渡到
RxJava1后,他们促使翻译
production的问题。 原文写于2017年12月28日,但总比没有发现要晚。
我们都是优秀的开发人员,使用
RxJava时会在
onError捕获错误。 这意味着我们已经保护自己免受应用程序崩溃的侵害,对吗?
不,不是真的。下面我们将看几个示例,其中即使正确实现了
onError ,应用程序也会由于
RxJava而
RxJava 。
RxJava基本错误处理RxJava
RxJava使用
RxJavaPlugins.onError作为基本错误处理程序。 它处理所有无法传递给订户的错误。 默认情况下,所有错误都会发送给它,因此可能会导致严重的应用程序崩溃。
2.0.6描述了此行为:
2.x设计的目标之一是没有丢失的错误。 有时序列在源引发onError之前结束或被取消。 在这种情况下,错误无处可去,它被发送到RxJavaPlugins.onError
如果RxJava没有基本的错误处理程序,则此类错误将对我们隐藏,开发人员将对代码中的潜在问题一无所知。
从版本
2.0.6开始,
RxJavaPlugins.onError聪明,并共享库/实现错误以及无法传递错误的情况。 分配给“错误”类别的错误按原样调用,其余错误包装在
UndeliverableException ,然后调用。 您可以
在这里看到所有这些逻辑(
onError和
isBug )。
新手在
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 规范相矛盾。
在单次调用终端事件( onError , onCompelete )之后,要求不再进行任何调用
事实证明,通过单次调用
onError不再可以
onError第二次调用。 当源中发生第二个错误时会发生什么? 它将被传递到
RxJavaPlugins.onError 。
进入这种情况的一种简单方法是使用
zip组合网络调用(例如,两个
Retrofit调用返回
Observable )。 如果两个调用都发生错误(例如,没有Internet连接),则两个源都将导致错误,第一个将属于
onError实现,第二个将传递给基本错误处理程序(
RxJavaPlugins.onError )。
没有订阅者的可连接示例
ConnectableObservable也可以引发
UndeliverableException 。 值得回顾的是,无论活动订户的存在与否,
ConnectableObservable都会引发事件,只需调用
connect()方法即可。 如果在没有订阅者的情况下
ConnectableObservable发生错误,它将被传递给基本错误处理程序。
这是一个很纯真的例子,可能会导致这样的错误:
someApi.retrofitCall()
如果
someApi.retrofitCall()导致错误(例如,没有Internet连接),则应用程序将崩溃,因为网络错误将传递给
RxJava基本错误
RxJava 。
这个示例似乎是虚构的,但是很容易陷入
ConnectableObservable仍处于连接状态但没有订阅者的情况。 我在调用API时使用
autoConnect()时遇到了这个问题。
autoConnect()不会自动禁用
Observable 。 我取消了
Activity的
onStop方法的订阅,但是在
Activity销毁后应用程序因
UndeliverableException而返回了网络调用的结果。
错误处理
那么如何处理这些错误呢?
第一步是查看发生的错误,并尝试确定导致这些错误的原因。 理想情况下,如果可以从
RxJavaPlugins.onError解决问题,以防止将该错误
RxJavaPlugins.onError给
RxJavaPlugins.onError 。
zipWith示例的解决方案是采用一个或两个源,并实现
其中一种捕获错误
的方法 。 例如,您可以使用
onErrorReturn传递默认值而不是错误。
ConnectableObservable示例更易于修复-只要确保在最后一个订阅者退订时断开
Observable的连接即可。 例如,
autoConnect()有一个重载的实现,该实现带有捕获连接时间的函数(
可以在此处查看更多信息 )。
解决问题的另一种方法是用您自己的替换基本错误处理程序。
RxJavaPlugins.setErrorHandler(Consumer<Throwable>)方法将帮助您。 如果这是适合您的解决方案,则可以捕获所有发送到
RxJavaPlugins.onError的错误,并根据需要进行处理。 该解决方案可能非常复杂-请记住,
RxJavaPlugins.onError会从应用程序中的所有
RxJava流接收错误。
如果您手动创建
Observable ,则可以调用
emitter.tryOnError()而不是
emitter.tryOnError() 。 仅当流没有终止并且具有订阅者时,此方法才会报告错误。 请记住,此方法是实验性的。
本文的主旨是,如果仅在订阅服务器中实现
onError ,则无法确定使用RxJava时没有错误。 您应该注意订户可能无法发现错误的情况,并确保已处理这些情况。