In Kotlin Coroutines, Dispatchers determine the thread or thread pool on which a coroutine will run. Choosing the right dispatcher ensures optimal performance and responsiveness.
๐ฆ Built-in Dispatchers
๐น Dispatchers.Main
Purpose: For UI-bound tasks.
Thread Context: Runs on the main/UI thread.
Available On: Android, JavaFX, Swing (requires a kotlinx-coroutines-android or kotlinx-coroutines-javafx dependency).
Use Cases:
- Updating UI after loading data
- Responding to user events (e.g. clicks)
โ ๏ธ Do not perform blocking or heavy computations here.
๐น Dispatchers.IO
Purpose: Designed for blocking I/O operations.
Thread Context: A shared pool of threads.
Scalability: Dynamically grows up to ~64 threads by default (more than CPU cores).
Use Cases:
- Reading/writing files
- Network or database calls (JDBC, Retrofit)
โ Blocking allowed โ suitable for legacy APIs.
๐น Dispatchers.Default
Purpose: For CPU-intensive operations.
Thread Context: Uses a thread pool with number of threads = number of CPU cores.
Use Cases:
- Complex algorithms
- Data crunching, sorting, parsing
๐ซ Avoid blocking I/O here โ designed for non-blocking compute tasks.
๐น Dispatchers.Unconfined (less common)
Purpose: Starts coroutine in the current thread but resumes in the thread of the suspending function.
Thread Context: No fixed thread.
Use Cases:
- For debugging or advanced coroutine control
- Lightweight coroutines not bound to any thread
โ ๏ธ Can lead to unexpected behavior; not recommended for UI or production use.
๐ ๏ธ Creating a Custom Dispatcher
val myDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
Use when:
- You want fine-grained control over threading (e.g. limiting concurrency)
- Need a dedicated thread pool for certain tasks
โ ๏ธ Donโt forget to close it after use:
myDispatcher.close()
โ Choosing the Right Dispatcher โ Summary Table
Dispatcher | Ideal For | Backed By | Blocking Allowed? | Scalability |
---|---|---|---|---|
Dispatchers.Main | UI updates, UI event handling | Main/UI thread | โ No | โ Low |
Dispatchers.IO | File, network, DB I/O | Large shared thread pool | โ Yes | โ High |
Dispatchers.Default | Calculations, CPU tasks | CPU core thread pool | โ No | โ Medium |
Dispatchers.Unconfined | Experimental, coroutine chaining | Callerโs thread (initially) | โ ๏ธ Depends | โ Low |
๐ก Best Practices
- Use Main only for UI work
- Use IO for anything blocking
- Use Default for heavy logic
- Avoid Unconfined unless you know what youโre doing
- Combine with
withContext(dispatcher)
to offload work
Related Articles
To learn more about Android development and concurrency:
- Jetpack Compose: Architecture - Understand how to structure your Android app
- Jetpack Compose: Composition Phase - Learn about UI rendering and state management
- Jetpack Compose: Custom Modifiers - Discover how to create reusable UI patterns