本文将讨论
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时没有错误。 您应该注意订户可能无法发现错误的情况,并确保已处理这些情况。