Featured image of post Dispatchers in Kotlin Coroutines

Dispatchers in Kotlin Coroutines

Simply put, a Dispatcher decides which thread a Coroutine will run on. It could be the main thread, a background thread, or it could push the Coroutine into a thread pool.

What is a Dispatcher?

Simply put, a Dispatcher decides which thread a Coroutine will run on. It could be the main thread, a background thread, or it could push the Coroutine into a thread pool.

There are 4 types of Dispatchers:

  • Dispatchers.Default
  • Dispatchers.Main
  • Dispatchers.IO
  • Dispatchers.Unconfined

Or you can create your own Dispatcher using newSingleThreadContext() or newFixedThreadPoolContext().

Coroutine builder functions like launch and async have a CoroutinesContext parameter so you can pass in a Dispatcher, since all the above Dispatchers extend from CoroutinesContext.

1
2
3
launch(Dispatchers.Default) {
    println("I'm working in thread ${Thread.currentThread().name}")
}

If you call launch or async without passing a CoroutinesContext, it will inherit the context of the CoroutineScope it was launched in.

1
2
3
4
5
fun main() = runBlocking<Unit> {
    launch {
        println("I'm working in thread ${Thread.currentThread().name}")
    }
}

In this example, it inherits the context of runBlocking, so it runs on the main thread.

Now, let’s look at each Dispatcher in detail.

Dispatchers.Default

Dispatchers.Default is used by default by builder functions like launch and async if you don’t assign another Dispatcher. Default uses a shared background threads pool. So you can use Dispatchers.Default for CPU-intensive work. For example:

  • Heavy tasks like matrix calculations
  • Sorting, filtering, or searching a huge list in memory
  • Bitmap operations in memory
  • Parsing JSON in memory

By default, the maximum number of threads used by Dispatchers.Default is equal to the number of CPU cores, but at least 2.

Dispatchers.Main

You can guess from the name, right? Exactly, Dispatchers.Main runs on the main thread, suitable for UI-related tasks.

Usually, Dispatchers.Main is a single thread.

Dispatchers.IO

Again, the name says it all. Dispatchers.IO uses a shared pool of threads created as needed. It helps offload blocking IO tasks. So it’s suitable for disk and network operations. For example:

  • Calling APIs
  • Downloading files from a server
  • Moving files between folders on disk
  • Reading and writing files
  • Querying databases
  • Loading Shared Preferences

The number of threads used by Dispatchers.IO is limited to 64 or the number of cores (whichever is greater).

Dispatchers.Unconfined

I call Dispatchers.Unconfined the wild horse, because you never know which thread it will run on.

Initially, when launched, the Coroutine runs on the thread that called it. But after being suspended and resumed, it may run on a different thread, depending on the suspend functions called. Dispatchers.Unconfined is suitable for work that doesn’t consume CPU and doesn’t update the UI. But the Kotlin documentation emphasizes:

The Unconfined dispatcher should not normally be used in code.

newSingleThreadContext

This function creates a new thread for you to play with. But creating a new thread is resource-intensive, and you have to call close to release it when done. So in practice, I don’t recommend using this.

There’s also newFixedThreadPoolContext to create a thread pool with a fixed size.

Comparison with RxJava, RxAndroid

You can see that Dispatchers are similar to Schedulers in RxJava.

CoroutinesRxJava/RxAndroid
Dispatchers.DefaultSchedulers.computation()
Dispatchers.MainAndroidSchedulers.mainThread()
Dispatchers.IOSchedulers.io()

Conclusion

In summary, Dispatchers are an important concept in Coroutines, so you need to understand them well to choose the right Dispatcher for each function.

Reference

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy