fun <V> callWithRetry(callable: Callable<V>, isRetryable: Predicate<Throwable?>): V { var failures = 0 while (true) { try { return callable.call() } catch (e: Throwable) { if (++failures == attempts || !isRetryable.apply(e)) { androidx.test.espresso.core.internal.deps.guava.base.Throwables.throwIfUnchecked( e ) throw RuntimeException(e) } logger.info(e, "Retrying transient error, attempt $failures") try { sleeper.sleep(Duration.millis(pow(2, failures) * 100)) } catch (e2: InterruptedException) { Thread.currentThread().interrupt() androidx.test.espresso.core.internal.deps.guava.base.Throwables.throwIfUnchecked( e ) throw RuntimeException(e) } } } }
Retries a unit of work in the face of transient errors . < p > Retrying is done a fixed number of times , with exponential backoff , if the exception that is thrown is deemed retryable by the predicate . If the error is not considered retryable , or if the thread is interrupted , or if the allowable number of attempts has been exhausted , the original exception is propagated through to the caller .