Bird
Raised Fist0
Kotlinprogramming~20 mins

Flow context preservation in Kotlin - Practice Problems & Coding Challenges

Choose your learning style10 modes available
Challenge - 5 Problems
🎖️
Flow Context Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
❓ Predict Output
intermediate
2:00remaining
What is the output of this Kotlin Flow code with context preservation?

Consider this Kotlin code using flow and withContext. What will it print?

Kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val flow = flow {
        emit(1)
        withContext(Dispatchers.IO) {
            emit(2)
        }
        emit(3)
    }
    flow.collect { value ->
        println(value)
    }
}
A
2
3
B
1
3
C
1
Exception thrown
D
1
2
3
Attempts:
2 left
💡 Hint

Think about how withContext affects the flow builder and emission.

❓ Predict Output
intermediate
2:00remaining
What happens if you emit inside a different coroutine context without preserving flow context?

What will this Kotlin code print?

Kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val flow = flow {
        emit(1)
        launch(Dispatchers.IO) {
            emit(2)
        }.join()
        emit(3)
    }
    flow.catch { e -> println("Error: $e") }
        .collect { value -> println(value) }
}
A
1
3
B
1
Error: java.lang.IllegalStateException: Flow invariant is violated
C
1
2
3
D
1
Exception thrown at emit(2)
Attempts:
2 left
💡 Hint

Consider what happens when emit is called from a different coroutine launched inside the flow builder.

🔧 Debug
advanced
2:30remaining
Why does this Kotlin Flow code cause a runtime error?

Identify the cause of the runtime error in this Kotlin Flow snippet:

Kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val flow = flow {
        emit(1)
        withContext(Dispatchers.IO) {
            launch {
                emit(2)
            }
        }
        emit(3)
    }
    flow.collect { println(it) }
}
ABecause emit(2) is called inside a new coroutine launched inside withContext, breaking flow context preservation
BBecause emit(3) is called after withContext block
CBecause withContext cannot be used inside flow builder
DBecause runBlocking cannot collect flows
Attempts:
2 left
💡 Hint

Look at where emit(2) is called and how coroutines are nested.

📝 Syntax
advanced
2:30remaining
Which option correctly preserves flow context when switching dispatcher?

Choose the Kotlin code snippet that correctly emits values on different dispatchers without breaking flow context preservation.

A
flow {
  emit(1)
  withContext(Dispatchers.IO) {
    launch {
      emit(2)
    }
  }
  emit(3)
}
B
flow {
  emit(1)
  launch(Dispatchers.IO) {
    emit(2)
  }
  emit(3)
}
C
flow {
  emit(1)
  withContext(Dispatchers.IO) {
    emit(2)
  }
  emit(3)
}
D
flow {
  emit(1)
  GlobalScope.launch(Dispatchers.IO) {
    emit(2)
  }
  emit(3)
}
Attempts:
2 left
💡 Hint

Remember that emit must be called from the flow's coroutine context.

🚀 Application
expert
3:00remaining
How many items are emitted by this Kotlin Flow with nested context switches?

Analyze the following Kotlin Flow code and determine how many items will be emitted and collected.

Kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val flow = flow {
        emit(1)
        withContext(Dispatchers.IO) {
            emit(2)
            withContext(Dispatchers.Default) {
                emit(3)
            }
        }
        emit(4)
    }
    val collected = mutableListOf<Int>()
    flow.collect { collected.add(it) }
    println(collected.size)
}
A4
B3
C2
DException thrown
Attempts:
2 left
💡 Hint

Consider how nested withContext calls affect flow emissions.

Practice

(1/5)
1. What does flowOn do in Kotlin Flow context preservation?
easy
A. It converts a flow into a list.
B. It cancels the flow immediately.
C. It collects the flow on the main thread only.
D. It changes the coroutine context where the flow is executed.

Solution

  1. Step 1: Understand flowOn purpose

    flowOn switches the coroutine context for upstream flow operations.
  2. Step 2: Identify effect on execution

    It changes the thread or dispatcher where the flow runs, preserving context safely.
  3. Final Answer:

    It changes the coroutine context where the flow is executed. -> Option D
  4. Quick Check:

    flowOn = changes coroutine context [OK]
Hint: Remember flowOn switches thread context upstream [OK]
Common Mistakes:
  • Thinking flowOn cancels the flow
  • Assuming flowOn collects on main thread only
  • Confusing flowOn with terminal operators
2. Which of the following is the correct syntax to switch the flow context to Dispatchers.IO?
easy
A. flow.flowOn(Dispatchers.Main)
B. flow.collectOn(Dispatchers.IO)
C. flow.flowOn(Dispatchers.IO)
D. flow.switchContext(Dispatchers.IO)

Solution

  1. Step 1: Recall correct function name

    The function to switch context in flow is flowOn.
  2. Step 2: Identify correct dispatcher

    To switch to IO thread, use Dispatchers.IO inside flowOn.
  3. Final Answer:

    flow.flowOn(Dispatchers.IO) -> Option C
  4. Quick Check:

    Correct syntax = flowOn(Dispatchers.IO) [OK]
Hint: Use flowOn with Dispatchers.IO to switch context [OK]
Common Mistakes:
  • Using collectOn or switchContext which don't exist
  • Passing wrong dispatcher like Dispatchers.Main
  • Misplacing flowOn after collect
3. What will be the output of this Kotlin code snippet?
val flow = flow {
  println("Emitting on: ${Thread.currentThread().name}")
  emit(1)
}.flowOn(Dispatchers.IO)

runBlocking {
  flow.collect {
    println("Collected $it on: ${Thread.currentThread().name}")
  }
}
medium
A. Emitting on: DefaultDispatcher-worker-1 Collected 1 on: main
B. Emitting on: main Collected 1 on: DefaultDispatcher-worker-1
C. Emitting on: main Collected 1 on: main
D. Emitting on: DefaultDispatcher-worker-1 Collected 1 on: DefaultDispatcher-worker-1

Solution

  1. Step 1: Identify where emission runs

    flowOn(Dispatchers.IO) moves emission to IO dispatcher thread, e.g., DefaultDispatcher-worker-1.
  2. Step 2: Identify where collection runs

    collect runs in runBlocking context, which is main thread.
  3. Final Answer:

    Emitting on: DefaultDispatcher-worker-1 Collected 1 on: main -> Option A
  4. Quick Check:

    Emission thread = IO, Collection thread = main [OK]
Hint: Emission runs on flowOn thread, collection on caller thread [OK]
Common Mistakes:
  • Assuming emission runs on main thread
  • Assuming collection runs on IO thread
  • Ignoring flowOn effect on emission
4. Identify the error in this Kotlin flow code:
val flow = flow {
  emit(1)
}.flowOn(Dispatchers.IO)

runBlocking {
  flow.flowOn(Dispatchers.Default).collect {
    println(it)
  }
}
medium
A. Multiple flowOn calls cause context override; only the last applies upstream.
B. flowOn cannot be used with runBlocking.
C. emit must be called inside collect block.
D. Dispatchers.Default is not a valid dispatcher.

Solution

  1. Step 1: Understand flowOn chaining behavior

    Multiple flowOn calls override upstream context; only the nearest upstream applies.
  2. Step 2: Identify effect on this code

    The second flowOn(Dispatchers.Default) overrides the first flowOn(Dispatchers.IO), so emission runs on Default, not IO.
  3. Final Answer:

    Multiple flowOn calls cause context override; only the last applies upstream. -> Option A
  4. Quick Check:

    Multiple flowOn = last one wins [OK]
Hint: Only one flowOn affects upstream; last one wins [OK]
Common Mistakes:
  • Thinking flowOn can be used multiple times independently
  • Believing emit must be inside collect
  • Assuming Dispatchers.Default is invalid
5. You want to perform a CPU-intensive calculation inside a flow without blocking the main thread, then update UI on the main thread. Which is the best way to preserve flow context?
hard
A. Perform calculation inside collect block on main thread.
B. Use flowOn(Dispatchers.Default) for calculation, then collect on main thread.
C. Use flowOn(Dispatchers.Main) for calculation, then collect on IO thread.
D. Use flowOn(Dispatchers.IO) for calculation, then collect on Default dispatcher.

Solution

  1. Step 1: Identify dispatcher for CPU-intensive work

    CPU-intensive tasks should run on Dispatchers.Default to avoid blocking main thread.
  2. Step 2: Preserve UI update on main thread

    Collecting on main thread allows safe UI updates after calculation.
  3. Final Answer:

    Use flowOn(Dispatchers.Default) for calculation, then collect on main thread. -> Option B
  4. Quick Check:

    CPU work on Default, UI update on Main [OK]
Hint: CPU work on Default dispatcher, collect on Main thread [OK]
Common Mistakes:
  • Running CPU work on Main dispatcher blocking UI
  • Collecting on background thread causing UI errors
  • Using IO dispatcher for CPU-intensive tasks
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard