Coroutine context and dispatchers.

Where do coroutines run? How do you control which thread executes your code? Today we explore coroutine context and dispatchers, the mechanisms that configure and schedule coroutines.

Every coroutine has a context.

CoroutineContext is a set of elements that configure the coroutine. It carries information about how the coroutine should behave.

Think of context as coroutine metadata. It answers questions like: what thread should this run on? What happens when it fails? What is its name?

Context is immutable. You do not modify a context. You create new contexts by combining elements.

The context travels with the coroutine. When you launch a child coroutine, it inherits context from its parent.

Context contains multiple elements, each with a purpose.

Dispatcher determines which thread or threads run the coroutine.

Job tracks the coroutine's lifecycle and enables cancellation.

CoroutineName gives the coroutine a name for debugging.

CoroutineExceptionHandler handles uncaught exceptions.

Each element has a key. You access elements by key: context bracket Job close bracket.

Elements combine with plus. Dispatchers dot IO plus CoroutineName of "worker" creates a context with both.

A dispatcher controls where coroutines execute.

Dispatchers dot Main runs coroutines on the main thread. Use this for UI updates in Android. Only one thread, so work must be quick.

Dispatchers dot IO is optimized for I/O operations. Network calls, file reads, database queries. Uses a shared pool of threads that can grow.

Dispatchers dot Default is for CPU-intensive work. Parsing, sorting, complex calculations. Uses a pool sized to CPU cores.

Each dispatcher is designed for specific work. Choose the right one for your task.

Specify a dispatcher when launching a coroutine.

launch Dispatchers dot IO runs the block on an IO thread.

async Dispatchers dot Default runs computation on the Default dispatcher.

If you do not specify, the coroutine inherits the dispatcher from its scope.

runBlocking uses the current thread as its dispatcher. launch inside runBlocking inherits this.

Most coroutine builders accept a context parameter. The dispatcher is part of that context.

Sometimes you need to change dispatchers mid-coroutine.

withContext switches the context for a block of code. withContext Dispatchers dot IO brace block close brace. The block runs on IO, then execution returns to the previous dispatcher.

Use this pattern: start on Main for UI, switch to IO for network, switch back to Main to update UI.

withContext is a suspend function. It suspends in the current context, resumes in the new context.

This is cleaner than launching separate coroutines. Your code stays sequential, but parts run on different dispatchers.

Child coroutines inherit context from their parent.

When you call launch inside a coroutine, the new coroutine gets the parent's context. Same dispatcher, same job tree.

You can override specific elements. launch Dispatchers dot IO inside a Main coroutine runs the child on IO. But other context elements still inherit.

Inheritance follows addition rules. Child context equals parent context plus any elements you specify. Specified elements override inherited ones.

This makes code cleaner. Set the context once at the top level. Children inherit without repetition.

Job is a context element that represents the coroutine's lifecycle.

Every coroutine has a Job. The Job tracks whether the coroutine is active, completed, or cancelled.

Jobs form a hierarchy. A parent Job has child Jobs. When you cancel the parent, all children cancel.

You can access the Job from context: coroutineContext bracket Job close bracket.

The Job enables structured concurrency. Parent-child relationships are tracked through Jobs in the context.

Access current context with coroutineContext inside a suspend function.

val currentDispatcher equals coroutineContext bracket ContinuationInterceptor close bracket.

Build context by adding elements: val myContext equals Dispatchers dot IO plus CoroutineName of "worker".

Contexts combine: context1 plus context2. If both have the same element type, the right side wins.

EmptyCoroutineContext is the identity. Adding it changes nothing.

Keep context building simple. Usually you just specify a dispatcher and maybe a name.

Let us summarize.

CoroutineContext configures how a coroutine runs.

Dispatchers determine which threads execute the coroutine: Main for UI, IO for input-output, Default for CPU work.

withContext switches dispatchers within a coroutine.

Child coroutines inherit context from parents.

Job is the context element that enables structured concurrency.

Choose your dispatcher wisely. The right dispatcher makes your code efficient.