私はこのようなレトロフィットでクラスを呼び出すために拡張メソッドを使用してネットワーク要求に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)どうすれば達成できますか?
再試行ロジック用のヘルパー 高階関数 を記述することをお勧めします。次の実装を開始に使用できます。
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
の実装を完全に変更することもできます。たとえば、すべての再試行パラメータをハードコーディングしたり、再試行回数の制限をなくしたり、デフォルトを変更したりできます。