If the blocking code is blocking because of CPU use, you should use Dispatchers.Default
. If it is network- or disk-bound, use Dispatchers.IO
. You can make this into a suspending function and wrap the blocking call in withContext
to allow this function to properly suspend when called from a coroutine:
suspend fun useTheLibrary(arg: String): String = withContext(Dispatchers.Default) {
BlockingLibrary.doSomething(arg)
}
If you need to use a specific ThreadPoolExecutor
because of API requirements, you can use asCoroutineDispatcher()
.
val myDispatcher = myExecutor.asCoroutineDispatcher()
//...
suspend fun useTheLibrary(arg: String): String = withContext(myDispatcher) {
BlockingLibrary.doSomething(arg)
}
If your library contains a callback-based way to run the blocking code, you can convert it into a suspend function using suspendCoroutine()
or suspendCancellableCoroutine()
. In this case, you don't need to worry about executors or dispatchers, because it's handled by the library's own thread pool. Here's an example in the Retrofit library, where they convert their own callback-based API into a suspend function.