RxJava 中的错误处理
在开发 Android 应用程序时,我们遇到了很多错误。即使是一行代码也可能导致错误,从而破坏整个项目。有许多不同类型的错误,每种都需要不同的处理方式,但在本文中,我们将特别关注 RxJava 中的错误处理。所以在此之前,让我们了解一些重要的事情。
- 什么是错误?
- 什么是 RxJava?
什么是错误?
错误是程序中的问题。它们可以被理解为错误或执行的输出,但该输出不是预期的。由于代码中的小错误,可能会发生错误。有时我们的整个程序会因为一个小错误而停止执行,但我们想处理那个错误并让我们的程序被执行,这就是为什么错误处理是必要的。
什么是 RxJava?
RxJava 是一个用于响应式函数式编程(如协程)的库。它允许我们通过使用可观察序列来执行异步任务并执行基于事件的程序。 RxJava 的依赖关系如下所示
implementation ‘io.reactivex.rxjava3:rxandroid:3.0.0’
implementation ‘io.reactivex.rxjava3:rxjava:3.0.0’
让我们看一个 RxJava 成功执行且没有错误的示例代码
在下面的代码中,onNext函数被调用,直到它打印所有整数,一旦它打印所有整数,就会调用 onComplete函数,程序成功执行。
Kotlin
Observable.just(1, 2, 3, 4)
.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete ", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(t: Int) {
Log.d("onNext ", t.toString())
}
override fun onError(e: Throwable) {
Log.d("onError ", e.localizedMessage)
}
})
Kotlin
Observable.just(1,2,3,4).subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete ", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
if (i == 3) {
throw NullPointerException("Its a Null Pointer Exception")
}
Log.d("onNext ", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError ", e.localizedMessage)
}
})
Kotlin
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
if (it == 2) {
throw (RuntimeException("Exception on 2"))
}
}.onExceptionResumeNext {
Log.d("onExceptionResumeNext : ","1")
}
.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete ", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
Log.d("onNext : ", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError", e.localizedMessage)
}
})
Kotlin
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map { x ->
if (x == 2) {
throw NullPointerException("Its a NPE")
}
x * 10
}.onErrorResumeNext { throwable: Throwable ->
return@onErrorResumeNext ObservableSource {
Log.d("onErrorResumeNext", throwable.localizedMessage)
}
}.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
Log.d("onNext", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError", e.localizedMessage)
}
})
Kotlin
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
if (it == 2) {
throw (RuntimeException("Exception on 2"))
}
}
.doOnError {
Log.d("doOnError", "Exception Occured")
}.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
Log.d("onNext", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError", e.localizedMessage)
}
})
Kotlin
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
if (it == 2) {
throw (RuntimeException("Exception on 2"))
}
}
.onErrorReturnItem(10)
.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete ", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
Log.d("onNext", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError", e.localizedMessage)
}
})
Kotlin
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
if (it == 2) {
throw (RuntimeException("Exception on 2"))
}
}
.onErrorReturn { t: Throwable ->
Log.d("onComplete", t.localizedMessage)
5
}.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
Log.d("onNext", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError", e.localizedMessage)
}
})
输出:
/onNext: 1
/onNext: 2
/onNext: 3
/onNext: 4
/onComplete: Completed
现在让我们看一个程序,我们故意在特定条件下抛出异常
在下面的代码中,当整数为 3 时,我们将抛出空指针异常。一旦我们得到异常,onNext函数就不会被调用并且代码停止执行或终止。
科特林
Observable.just(1,2,3,4).subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete ", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
if (i == 3) {
throw NullPointerException("Its a Null Pointer Exception")
}
Log.d("onNext ", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError ", e.localizedMessage)
}
})
输出:
Its a Null Pointer Exception
在 RxJava 中使用不同的 RxJava运算符处理错误
在 RxJava 中,运算符是允许您操作 Observables 发出的数据的组件。以下是 RxJava 中用于处理错误的运算符,我们将逐一了解它们。
- onExceptionResumeNext()
- onErrorResumeNext()
- doOnError()
- onErrorReturnItem()
- onErrorReturn()
1. onExceptionResumeNext()
如果这个运算符在 onComplete、onError 和 onNext 开始之前就发生了异常,那么它将直接转到 onExceptionResumeNext 并执行 onExceptionResumeNext() 范围内的任务。让我们借助以下代码更详细地了解这一点。
科特林
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
if (it == 2) {
throw (RuntimeException("Exception on 2"))
}
}.onExceptionResumeNext {
Log.d("onExceptionResumeNext : ","1")
}
.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete ", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
Log.d("onNext : ", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError", e.localizedMessage)
}
})
输出:
onNext : 1
onExceptionResumeNext : 1
当 OnNext() 第一次被调用并打印 1 并且返回值 2 时, onExceptionResumeNext 被调用,因此在其中打印 1 的代码被执行。
2. OnErrorResumeNext()
该运算符处理程序生成的 throwable。让我们看一下下面的代码。
科特林
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map { x ->
if (x == 2) {
throw NullPointerException("Its a NPE")
}
x * 10
}.onErrorResumeNext { throwable: Throwable ->
return@onErrorResumeNext ObservableSource {
Log.d("onErrorResumeNext", throwable.localizedMessage)
}
}.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
Log.d("onNext", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError", e.localizedMessage)
}
})
输出:
onNext: 10
onErrorResumeNext: Its a NPE
当 onNext 被调用并在 map运算符中将第一个整数与 10 相乘后打印第一个整数时,当值 2 出现时,它会引发 NPE 异常。它不会直接移动到 onError 并且错误将在 onErrorResumeNext 中处理,我们可以在其中执行其余操作。
3.doOnError()
doOnError运算符与其他运算符符有些不同。我们可以称其为副作用运算符,在此运算符的情况下,用户将在异常发生之前看到错误。
科特林
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
if (it == 2) {
throw (RuntimeException("Exception on 2"))
}
}
.doOnError {
Log.d("doOnError", "Exception Occured")
}.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
Log.d("onNext", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError", e.localizedMessage)
}
})
输出:
onNext: 1
doOnError: Exception Occurred
onError: Exception on 2
在上面的输出中,第一个 1 是从 onNext 打印的,然后当值达到 2 时,就会发生异常。由于我们调用了副作用运算符,所以 doOnError 在 onError 方法之前首先被调用。如果我们不使用副作用运算符,则应该调用 OnError运算符。所以简而言之,副作用运算符是那些在 onNext()、onError() 等主要方法之前被调用的运算符。
4. onErrorReturnItem()
每当发生异常或错误时,此运算符都会返回一个项目,然后是 onComplete()运算符。
科特林
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
if (it == 2) {
throw (RuntimeException("Exception on 2"))
}
}
.onErrorReturnItem(10)
.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete ", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
Log.d("onNext", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError", e.localizedMessage)
}
})
输出:
onNext: 1
onNext: 10
onComplete: Completed
当 onNext() 被调用并且值 1 被打印出来时,当 doOnNext() 中的值变为 2 时,我们得到一个异常。但是当我们使用 onErrorReturnItem()运算符时,它会在发生相同数据类型的异常时返回一个项目。在这里,由于我们使用的是整数数据类型,所以它会在 onNext()运算符中返回一个整数数据类型的项,然后它会最后调用 onComplete()运算符。
5. onErrorReturn()
有时我们需要在发生错误或异常时生成默认项。因此,我们使用了 onErrorReturn(),它提供了一个 lambda 和一个 throwable 来返回。让我们看看下面的代码
科特林
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
if (it == 2) {
throw (RuntimeException("Exception on 2"))
}
}
.onErrorReturn { t: Throwable ->
Log.d("onComplete", t.localizedMessage)
5
}.subscribe(object : Observer {
override fun onComplete() {
Log.d("onComplete", "Completed")
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(i: Int) {
Log.d("onNext", i.toString())
}
override fun onError(e: Throwable) {
Log.d("onError", e.localizedMessage)
}
})
输出:
onComplete: Exception on 2
onNext: 5
onComplete: Completed
onNext 在捕获异常后被调用并打印 5,然后完成流程。我们可以看到我们在 onErrorReturn运算符中传递了值 5。如果我们不传递一个值,我们将得到一个默认值。
这样我们就可以处理错误,尤其是在 RxJava 中。