How Retrofit Turns Callbacks into Kotlin Suspend Functions
Understanding that Retrofit's suspend support is a callback-to-continuation bridge, not a blocking call, prevents developers from mistakenly wrapping every Retrofit call in withContext(Dispatchers.IO) and adding unnecessary thread switches.
When a Retrofit interface declares a suspend function, the Kotlin compiler adds a hidden Continuation parameter. Retrofit's HttpServiceMethod inspects the method signature, detects that Continuation, and routes the call to a SuspendForBody adapter. That adapter wraps OkHttp's enqueue callback inside suspendCancellableCoroutine, suspending the coroutine until the network response arrives, then resuming it with the deserialized body or an exception. The thread is never blocked; OkHttp's dispatcher handles the I/O, and the coroutine dispatcher handles resumption. Because failures are delivered via resumeWithException, callers can catch network errors with a plain try-catch block, exactly as if the exception were thrown locally.
The Continuation-parameter check is a pragmatic, low-ceremony way for a Java library to detect Kotlin suspend functions without a Kotlin dependency at compile time.
Many developers add withContext(Dispatchers.IO) to Retrofit calls out of habit, but Retrofit already hands I/O to OkHttp's thread pool, so the extra context switch is pure overhead.
The naming collision between Retrofit's Call.await() and Deferred.await() causes persistent confusion, even though one bridges callbacks and the other waits on a coroutine result.