Reputation: 713
I am using OkHttp to make a synchronous http request. To avoid blocking the main thread, I wrapped the blocking network call in a suspend function and withContext(Dispatchers.IO)
suspend fun run(): String {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build()
return withContext(Dispatchers.IO) {
val response = client.newCall(request).execute()
return@withContext "Request completed successfully"
}
}
Android Studio gives me a warning that execute()
is an "Inappropriate blocking method call". My understanding is that execute()
will block during the http request taking up a thread in Dispatchers.IO for the duration of the request, which is not ideal. To avoid this issue, I can use the asynchronous version of the request wrapped in suspendCoroutine
suspend fun runAsync(): String = suspendCoroutine { continuation ->
val client = OkHttpClient()
val request = Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
continuation.resumeWithException(e)
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
continuation.resume("Request completed successfully")
}
}
})
}
This avoids the warning, but I do not understand how it is functionally different than the synchronous version above. I am assuming that the async version of the http call uses a thread to wait on the request. Is this assumption correct? If not, how does the async function wait for the callback to return?
Upvotes: 3
Views: 1297
Reputation: 200138
I do not understand how it is functionally different than the synchronous version above. I am assuming that the async version of the http call uses a thread to wait on the request. Is this assumption correct? If not, how does the async function wait for the callback to return?
It's not correct, the point of the async approach is that there are no threads being blocked while the call takes place. Your second approach achieves just that -- when the coroutine suspends, it doesn't live on any thread at all. Depending on the implementation details of OkHttp
, it may or may not be the case that some internal thread is blocked because it relies on blocking IO. Ideally it should be implemented in terms of Java NIO (more typically, through the Netty library) and rely on the low-level non-blocking primitive called the Selector.
The correct approach is the way you did it with implementing suspension, and kotlin-coroutines-okhttp already provides exactly that implementation in its Call.await()
function.
Upvotes: 3