DispatcherModule
Hilt module providing qualified coroutine dispatchers for dependency injection.
Why This Module Exists
While Kotlin provides Dispatchers.IO, Dispatchers.Default, and Dispatchers.Main directly, injecting them via Hilt offers several advantages:
Testability: Tests can inject
TestDispatcherto control coroutine executionFlexibility: Can swap dispatcher implementations without changing consuming code
Consistency: Enforces using the correct dispatcher for each task type
Type Safety: Qualifiers prevent accidental dispatcher misuse
Usage Guidelines
IoDispatcher - For I/O Operations
Use for blocking I/O tasks:
Network requests (Retrofit calls, HTTP downloads)
Database operations (Room queries, inserts, updates)
File system operations (reading/writing files)
DataStore reads/writes
class Repository @Inject constructor(
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
) {
suspend fun fetchData() = withContext(ioDispatcher) {
database.query() // Runs on IO dispatcher
}
}DefaultDispatcher - For CPU-Intensive Tasks
Use for computational work:
JSON parsing (large payloads)
Data transformation (mapping large lists)
Image processing
Complex calculations
class DataProcessor @Inject constructor(
@DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher
) {
suspend fun processLargeDataset() = withContext(defaultDispatcher) {
data.map { /* complex transformation */}
}
}MainDispatcher - For UI Updates
Rarely needed in this architecture! Most UI updates happen via:
StateFlow.collectAsStateWithLifecycle()(automatically dispatches to Main)Compose recomposition (automatically on Main thread)
Only use when you need explicit main thread dispatch:
class LegacyViewHelper @Inject constructor(
@MainDispatcher private val mainDispatcher: CoroutineDispatcher
) {
suspend fun updateView() = withContext(mainDispatcher) {
// Direct view manipulation
}
}Architecture Decision
This template uses injected dispatchers instead of hardcoded Dispatchers.IO because:
Repositories can be unit tested with
StandardTestDispatcherTests run synchronously and deterministically
No need for
runTestoradvanceUntilIdle()when dispatchers are injectedFuture flexibility to add custom dispatchers (e.g., limited concurrency)