当我们讨论在Kotlin Flow中使用运算符重载任务时,我们将讨论以下两个运算符:
retryWhen
retry
这两种运算符在大多数情况下都可以互换使用,今天我们将学习它们。
retryWhen
fun <T> Flow<T>.retryWhen(predicate: suspend FlowCollector<T>.(cause: Throwable, attempt: Long) -> Boolean): Flow<T>
并且,我们使用如下运算符:
.retryWhen { cause, attempt ->
}
这里,我们有两个参数,如下所示:
原因:这个cause是可抛出它是所有错误和异常的基类。
尝试:这个attempt表示当前尝试的数字。它从零 。
例如,如果启动任务时出现异常,我们将收到原因(异常)和尝试(0)。
这个retryWhen需要一个谓语函数来决定是否重试。
如果predicate函数返回是的,则只有它会重试,否则它不会。
例如,我们可以这样做:
.retryWhen { cause, attempt ->
if (cause is IOException && attempt < 3) {
delay(2000)
return@retryWhen true
} else {
return@retryWhen false
}
}
在这种情况下,我们回来了true当原因是IO异常,以及attempt计数小于3 .
因此,只有满足条件时才会重试。
注意:作为predicate函数是挂起函数,可以从中调用另一个挂起函数。
如果我们注意到上面的代码,我们已经调用delay(2000),以便仅在延迟后重试2秒。
retry
fun <T> Flow<T>.retry(
retries: Long = Long.MAX_VALUE,
predicate: suspend (cause: Throwable) -> Boolean = { true }
): Flow<T>
来自Kotlin流源代码的完整块。
fun <T> Flow<T>.retry(
retries: Long = Long.MAX_VALUE,
predicate: suspend (cause: Throwable) -> Boolean = { true }
): Flow<T> {
require(retries > 0) { "Expected positive amount of retries, but had $retries" }
return retryWhen { cause, attempt -> attempt < retries && predicate(cause) }
}
如果我们看到retry函数,它实际上调用重述时间内部。
retry函数具有默认参数。
如果我们不通过retries,它将使用Long.MAX_值 .
如果我们不通过predicate,它将提供真实的。
例如,我们可以这样做:
.retry()
它将继续重试,直到任务成功完成。
例如,我们也可以这样做:
.retry(3)
它只会重试3。
例如,我们也可以这样做:
.retry(retries = 3) { cause ->
if (cause is IOException) {
delay(2000)
return@retry true
} else {
return@retry false
}
}
在这里,它变得非常类似于我们使用retryWhen以上。
我们回来了true当原因是IOException。因此,只有当原因是IOException .
如果我们注意到上面的代码,我们已经调用delay(2000),因此仅在延迟2秒后重试。
现在,让我们看看代码示例。
这是一个函数,用于模拟长时间运行的异常任务。
private fun doLongRunningTask(): Flow<Int> {
return flow {
// your code for doing a long running task
// Added delay, random number, and exception to simulate
delay(2000)
val randomNumber = (0..2).random()
if (randomNumber == 0) {
throw IOException()
} else if (randomNumber == 1) {
throw IndexOutOfBoundsException()
}
delay(2000)
emit(0)
}
}
现在,当使用retry操作
viewModelScope.launch {
doLongRunningTask()
.flowOn(Dispatchers.Default)
.retry(retries = 3) { cause ->
if (cause is IOException) {
delay(2000)
return@retry true
} else {
return@retry false
}
}
.catch {
// error
}
.collect {
// success
}
}
同样,当使用retryWhen操作
viewModelScope.launch {
doLongRunningTask()
.flowOn(Dispatchers.Default)
.retryWhen { cause, attempt ->
if (cause is IOException && attempt < 3) {
delay(2000)
return@retryWhen true
} else {
return@retryWhen false
}
}
.catch {
// error
}
.collect {
// success
}
}
如果我们看到,每次我们加上延迟2秒,但在实际的用例中,我们添加了指数退避的延迟。别担心,我们也会执行的。
具有指数退避延迟的重试算子
viewModelScope.launch {
var currentDelay = 1000L
val delayFactor = 2
doLongRunningTask()
.flowOn(Dispatchers.Default)
.retry(retries = 3) { cause ->
if (cause is IOException) {
delay(currentDelay)
currentDelay = (currentDelay * delayFactor)
return@retry true
} else {
return@retry false
}
}
.catch {
// error
}
.collect {
// success
}
}
在这里,我们创建了两个变量:
电流延迟:这表示要在当前重试中使用的延迟。
延迟因子:我们用这个delayFactor把它乘以电流延迟增加下一次重试的延迟。
就这样,我们实现了具有指数退避延迟的重试。
您可以构建、运行和播放所提供项目中的所有示例。
这样我们就可以使用retry和重述时间解决Android应用开发中的有趣问题。请记住,在大多数我们在Android应用程序开发中解决的情况下,两者都可以互换使用。