Coroutine is a design pattern in Kotlin used to simplify asynchronous execution. In Android coroutines help manage long-running tasks that might block the main thread. Coroutines provides solution to two main problems, the main-safety and long running tasks. Performing long-running tasks on main threads e.g. making network calls might otherwise cause the main thread to wait or block until a response is received and as a result cause the app to be unresponsive.
Coroutine Dispatcher
Coroutines execute in some context which includes dispatchers. A coroutine dispatcher determines what thread a corresponding coroutine will use for its execution.
Kotlin provides three coroutine dispatchers you can use.
Dispatchers.Main - Used to perform interactions with the UI and perform quick operations. It runs on the main thread. A good example of this kind of dispatch is the
suspend
functions.Dispatchers.IO - This dispatcher is optimized to perform disk or network I/O outside of the main thread. Indicates that a coroutine should be executed on a thread reserved for I/O operations. Examples include reading from or writing to files, and running any network operations.
Dispatchers.Default - Performs CPU intensive work outside of main thread.
Let's dive into some code and look at a simple implementation of the three.
suspend fun fetchDocs() { // Dispatchers.Main
val result = get("developer.android.com") // Dispatchers.Main
show(result) // Dispatchers.Main
}
get()
performs a network request but still runs on the main thread, its's because the execution is done in a suspend
function. The coroutine is therefore suspended/paused before the network execution begins and resumed when the execution is complete instead of using a traditional approach which involves using a callback to notify the main thread.
Switching coroutine context
The function withContext()
provides a way to switch between coroutine context. Inside the function body above we can call withContext(Dispatchers.IO)
to create a block that runs on the IO thread pool. Every code on this block will therefore be executed via the IO
dispatcher. Our function will then look like below
suspend fun get(url: String) = // Dispatchers.Main
withContext(Dispatchers.IO) { // Dispatchers.IO (main-safety block)
/* perform network IO here */
}
}
A function is considered to be main-safe in coroutines if it doesn't block UI updates on the main thread. The withContext(Dispatchers.IO)
makes sure that UI updates on the main thread is not blocked.