Wie man exponentielles Backoff wiederholt bei Kotlin-Coroutinen versucht
Lesezeit: 5 Minuten
shakil.k
Ich verwende Kotlin-Coroutinen für Netzwerkanfragen mit der Erweiterungsmethode, um die Klasse wie folgt nachzurüsten
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
}
})
registerOnCompletion(continuation)
}
}
dann verwende ich von der aufrufenden Seite die obige Methode wie folgt
Ich möchte diesen API-Aufruf in einigen Fällen exponentiell wiederholen, dh (IOException), wie kann ich das erreichen?
Ich würde vorschlagen, einen Helfer zu schreiben Funktion höherer Ordnung für Ihre Wiederholungslogik. Sie können die folgende Implementierung für den Anfang verwenden:
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
}
Die Verwendung dieser Funktion ist sehr einfach:
val networkResult = retryIO { api.getArticle().await() }
Sie können Wiederholungsparameter von Fall zu Fall ändern, zum Beispiel:
val networkResult = retryIO(times = 3) { api.doSomething().await() }
Sie können die Implementierung auch komplett ändern retryIO um den Anforderungen Ihrer Anwendung gerecht zu werden. Sie können beispielsweise alle Wiederholungsparameter fest codieren, die Begrenzung der Anzahl der Wiederholungen aufheben, die Standardeinstellungen ändern usw.
Das war etwas, das mir seit einigen Tagen im Hinterkopf lag. Schön zu sehen, dass die Lösung nicht komplexer ist, als ich mir vorgestellt habe. Ich habe mich auch gefragt, ob es sinnvoll wäre, diese Hilfsfunktion als Inline-Funktion zu definieren. Und last but not least: wie würde das a modifiziert werden, wenn Sie den Retry erst nach Aufforderung durch den Benutzer (zB in einem Dialog) ausführen wollen?