【问题标题】:RuntimeException handling best practicesRuntimeException 处理最佳实践
【发布时间】:2016-12-19 13:17:20
【问题描述】:

RuntimeExceptions 应该表示编程错误,我希望我的应用程序在我的 observables 中的某些东西抛出 RuntimeException 时崩溃。

最好的方法是什么?现在我正在考虑这个解决方案(它是 Kotlin,但我希望它可以理解)

fun <T> Observable<T>.subscribeCrashOnRuntimeException(onNext: (T) -> Unit, onError: (Throwable) -> Unit) {
  this.subscribe({
    onNext(it)
  }, { e ->
    if (e is RuntimeException) {
      throw e
    } else {
      onError(e)
    }
  })
}

fun usageExample() {
  val observable = Observable.just(1)
  observable.subscribeCrashOnRuntimeExceptions(
    { next -> Log.d("TAG", "next: $next") },
    { e -> Log.d("TAG", "error: $e") }
  )
}

但我对此表示怀疑。例如,使用此解决方案很难偶尔“捕获”特定的 RuntimeExceptions。也许有一种众所周知的方法来处理我只是不知道如何 google 的情况?

【问题讨论】:

  • 阅读onErrorResumeNext,您可以在其中决定如何处理错误。
  • RuntimeExceptions are supposed to indicate programming error - 不一定。丢失的文件是编程错误吗?是网络连接断开了吗?大多数时候,您明确不想崩溃,而是减轻或重试。
  • @TassosBassoukos 丢失的文件不是 RuntimeExceptions 对于断开的网络连接也是如此。好像你不明白什么是 RuntimeExceptions。您似乎认为它只是 Exception 的同义词。

标签: android rx-java rx-kotlin


【解决方案1】:

我认为在处理运行时(又名未检查)或常规(又名已检查)异常方面应该没有太大区别。如今,两者都被广泛使用,并且可以根据特定情况恢复或不可恢复。

处理错误的反应式方法是:

  1. 通过onErrorResumeNextonErrorReturn运营商;这些允许检查错误并可能从中恢复
  2. 通过retry* 系列运营商;这些允许检查错误并可能通过重新订阅(例如重试网络调用)从错误中恢复
  3. 通过onError您的订阅者回调;顺便说一句,如果您不提供这样的回调,错误将以常规 Java 方式重新抛出,因此您的程序崩溃

相关话题:How to handle different kinds of errors in Retrofit Rx onError without ugly instanceof

还要注意以常规 Java 方式抛出异常的缺点:

  • 消息处理过程中的调用栈与定义消息处理规则时的调用栈不同;这意味着捕获此类异常以及解释堆栈跟踪可能非常困难
  • 调度程序捕获的异常可能不会导致程序终止;即你的程序可能最终会挂在损坏的状态

示例代码

Observable.fromCallable(() -> {
    ...
    if (ok) return "Success!";
    else throw new RuntimeException("Failure at source");
})
.map(s -> {
    ... processing is bypassed in case of an error
})
.map(s -> {
    ...
    if (...) return s.upperCase();
    else throw new RuntimeException("Failure during processing");
})
.onErrorReturn(e -> {
    if (e.getMessage().contains("processing"))
        return "Recovered";
    throw Exceptions.propagate(e); // let it continue as an error
})
.subscribe(s -> println("got result: " + s),
           e -> println("got error: " + e);

所有异常都被 RxJava 捕获并沿定义的路径传递。

onError* 运算符的作用类似于中间 catch 块。

订阅者的onError 回调就像顶级catch 块。

有关该主题的更多链接:

【讨论】:

  • 假设我有一些 observable 应该返回数据或错误。例如执行 http 请求的 observable。这两种返回类型都是有效的,订阅者应该能够对它们做出反应。因此,据我了解,处理这种情况的“官方”RxJava 方法是返回“数据或错误”对象。并且不要提供 onError() 回调,因此应用程序会因任何意外错误而崩溃。我对吗?感觉这种方法会导致大量的样板文件,但也许我错了,我猜应该尝试一下。
  • @DmitryRyadnenko 执行 http 请求的 Observable 应该 return 响应或 throw 异常,这将被 RxJava 捕获并作为错误,最终会出现在订阅者的onError 回调中。
  • 这就是我现在做事的方式,我最终在每个 onError 方法中都使用了相同的样板。就像gist.github.com/rongi/3955d4946231c195d04adf110928a2ed 这污染了我原本简单干净的订阅代码,所以我提出了问题中描述的解决方案。这使我的订阅者代码再次干净。你有同样的问题吗?你认为这是一个问题吗?你如何解决这个问题?
  • @DmitryRyadnenko 如果多个订阅者的错误处理程序相同,那么您可以将其提取到一个公共函数或类中。或者干脆省略错误处理程序,在应用程序的顶层添加catch
猜你喜欢
  • 2013-08-25
  • 2018-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-27
  • 2015-09-25
  • 2017-05-11
相关资源
最近更新 更多