android - 如何在 kotlin 协程上进行指数退避重试

标签 android kotlin async-await retrofit2 kotlin-coroutines

我正在使用 kotlin 协程进行网络请求,使用扩展方法调用改造中的类,像这样

public suspend fun <T : Any> Call<T>.await(): T {

  return suspendCancellableCoroutine { continuation -> 

    enqueue(object : Callback<T> {

        override fun onResponse(call: Call<T>?, response: Response<T?>) {
            if (response.isSuccessful) {
                val body = response.body()
                if (body == null) {
                    continuation.resumeWithException(
                            NullPointerException("Response body is null")
                    )
                } else {
                    continuation.resume(body)
                }
            } else {
                continuation.resumeWithException(HttpException(response))
            }
        }

        override fun onFailure(call: Call<T>, t: Throwable) {
            // Don't bother with resuming the continuation if it is already cancelled.
            if (continuation.isCancelled) return
            continuation.resumeWithException(t)
        }
    })

      registerOnCompletion(continuation)
  }
}

然后从调用方我使用上面这样的方法

private fun getArticles()  = launch(UI) {

    loading.value = true
    try {
        val networkResult = api.getArticle().await()
        articles.value =  networkResult

    }catch (e: Throwable){
        e.printStackTrace()
        message.value = e.message

    }finally {
        loading.value = false
    }

}

我想在某些情况下以指数方式重试这个 api 调用,即(IOException)我怎样才能实现它??

最佳答案

我建议写一个助手 higher-order function为您的重试逻辑。您可以使用以下实现作为开始:

suspend fun <T> retryIO(
    times: Int = Int.MAX_VALUE,
    initialDelay: Long = 100, // 0.1 second
    maxDelay: Long = 1000,    // 1 second
    factor: Double = 2.0,
    block: suspend () -> T): T
{
    var currentDelay = initialDelay
    repeat(times - 1) {
        try {
            return block()
        } catch (e: IOException) {
            // you can log an error here and/or make a more finer-grained
            // analysis of the cause to see if retry is needed
        }
        delay(currentDelay)
        currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay)
    }
    return block() // last attempt
}

使用这个函数很简单:

val networkResult = retryIO { api.getArticle().await() }

您可以根据具体情况更改重试参数,例如:

val networkResult = retryIO(times = 3) { api.doSomething().await() }

您还可以完全更改 retryIO 的实现以适应您的应用程序的需要。例如,您可以硬编码所有重试参数,摆脱重试次数限制,更改默认值等。

关于android - 如何在 kotlin 协程上进行指数退避重试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46872242/

相关文章:

java - Null 对象引用上的 SupportMapFragment#getMapAsync()

spring - 在Docker中运行嵌入式mongo时org.springframework.beans.factory.UnsatisfiedDependencyException

android - 在 android recyclerview 中一次仅捕捉一项,而不捕捉更多项目

C# async/await - 限制调用异步方法/锁定的次数

javascript - 处理用于加载 .gif 图像的异步函数的正确方法是什么?

android - 将 ProGuard/Dexguard 与多个 Android Studio 模块一起使用

android - 无法使用 Firebase 控制台发送数据消息

c# - 使用 Task.Run 调用异步方法似乎是错误的?

android - 如何在 htmlcleaner 或 jSoup 中提取标签内的文本

安卓相机X |颜色检测