web-dev-qa-db-fra.com

Comment réessayer une interruption exponentielle sur les coroutines kotlin

J'utilise des coroutines kotlin pour la demande de réseau en utilisant la méthode d'extension pour appeler la classe en retrofit comme ceci

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)
  }
}

puis du côté appelant j'utilise la méthode ci-dessus comme celle-ci

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
    }

}

je veux réessayer exponentiellement cet appel api dans certains cas, c'est-à-dire (IOException) comment puis-je y arriver ??

23
shakil.k

Je suggérerais d'écrire une aide fonction d'ordre supérieur pour votre logique de nouvelle tentative. Vous pouvez utiliser l'implémentation suivante pour commencer:

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
}

L'utilisation de cette fonction est très simple:

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

Vous pouvez modifier les paramètres de nouvelle tentative au cas par cas, par exemple:

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

Vous pouvez également modifier complètement l'implémentation de retryIO pour l'adapter aux besoins de votre application. Par exemple, vous pouvez coder en dur tous les paramètres de nouvelle tentative, vous débarrasser de la limite du nombre de nouvelles tentatives, modifier les valeurs par défaut, etc.

78
Roman Elizarov