Interview QuestionPractical QuestionFollow-up Questions

Cold Flow vs Hot Flow

skydovesJaewoong Eum (skydoves)||9 min read

Cold Flow vs Hot Flow

Kotlin's Flow API provides two fundamentally different emission models. A cold flow produces values only when a collector is present, starting fresh for each new collector. A hot flow emits values independently of collectors, and subscribers observe only the values emitted after they begin collecting. Understanding this distinction is essential for choosing the right flow type for network requests, database queries, UI state, and event broadcasting. By the end of this lesson, you will be able to:

  • Explain how cold flows defer execution until collection.
  • Describe how hot flows maintain an active emission source shared across collectors.
  • Identify when to use each model in Android applications.
  • Recognize the lifecycle implications of cold and hot flows.

Cold Flows

A cold flow is created with the flow { } builder. No code inside the builder runs until a terminal operator like collect is called. Each collector triggers an independent execution of the builder block:

val coldFlow = flow {
    println("Flow started")
    emit(1)
    emit(2)
    emit(3)
}

coldFlow.collect { println("Collector A: $it") }
coldFlow.collect { println("Collector B: $it") }

The output shows "Flow started" twice because each collect call triggers a separate execution. Collector A and Collector B each receive all three values independently. There is no shared state between them.

This behavior makes cold flows ideal for operations that should run fresh for each consumer. Network requests, database queries, and file reads are natural fits because each consumer expects its own result. If two parts of the UI collect the same cold flow, two separate network requests execute.

Cold flows are also lazy. If no one collects, no work happens. This aligns well with lifecycle aware collection in Android, where flows are collected only while the UI is visible.

Standard flow operators like map, filter, flatMapConcat, and catch preserve cold flow semantics. Each operator adds a transformation layer, but the entire chain remains lazy and runs independently for each collector. This composability is one of the main strengths of cold flows:

val users: Flow<List<User>> = flow {
    emit(api.fetchUsers())
}.map { response ->
    response.filter { it.isActive }
}.catch { e ->
    emit(emptyList())
}

Each collector triggers the full chain from the API call through filtering and error handling. This independence means that errors in one collector's chain do not affect other collectors, and cancelling one collection does not cancel another.

Hot Flows

Hot flows emit values regardless of whether any collector is active. Kotlin provides two built in hot flow types: SharedFlow and StateFlow.

A SharedFlow broadcasts values to all active collectors simultaneously. Collectors that subscribe after an emission miss it unless the replay parameter is configured:

val sharedFlow = MutableSharedFlow<Int>(replay = 0)

scope.launch {
    sharedFlow.emit(1)
    sharedFlow.emit(2)
}

// A collector subscribing after emissions misses them
sharedFlow.collect { println("Received: $it") }

This interview continues for subscribers

Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.

Become a Sponsor