Convertire un Flow cold in hot con shareIn e stateIn
Trasformi Flow cold in stream hot con gli operatori di condivisione.
Convertire un Flow cold in hot con shareIn e stateIn è una lezione Kotlin Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Kotlin Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Kotlin Academy include 4 lezioni in totale.
Flussi freddi e caldi
I flussi freddi ripartono per ogni collettore, mentre i flussi caldi condividono un'unica sottoscrizione upstream. Convertire un flusso freddo in uno caldo evita lavoro ridondante quando si sottoscrivono più collettori.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun expensiveFlow() = flow {
println("Starting upstream work") // runs once if shared
repeat(3) { delay(100); emit(it) }
}
fun main() = runBlocking {
val cold = expensiveFlow()
// Two collectors = two executions:
launch { cold.collect { } }
launch { cold.collect { } }
delay(500)
coroutineContext.cancelChildren()
}Fondamenti di shareIn
flow.shareIn(scope, started, replay) converte un Flow freddo in uno SharedFlow, condividendo un'unica sottoscrizione upstream.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val cold = flow {
println("Upstream started once")
repeat(5) { delay(100); emit(it) }
}
val hot = cold.shareIn(this, SharingStarted.Eagerly, replay = 0)
launch { hot.collect { println("A: $it") } }
launch { hot.collect { println("B: $it") } }
delay(600)
coroutineContext.cancelChildren()
}Fondamenti di stateIn
flow.stateIn(scope, started, initialValue) converte il flusso in uno StateFlow, che ha sempre un valore e riproduce 1 elemento.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val cold = flow { delay(100); emit(42) }
val state: StateFlow<Int> = cold.stateIn(
scope = this,
started = SharingStarted.Eagerly,
initialValue = 0
)
println(state.value) // 0 immediately
delay(200)
println(state.value) // 42 after upstream emits
coroutineContext.cancelChildren()
}SharingStarted.Eagerly
Eagerly: l'upstream si avvia immediatamente quando viene chiamato shareIn/stateIn, indipendentemente dai sottoscrittori.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val flow = flow {
println("Eagerly started")
emit(1)
}.shareIn(this, SharingStarted.Eagerly)
// Upstream already running even before any collect
delay(50)
launch { flow.collect { println(it) } }
delay(100)
coroutineContext.cancelChildren()
}SharingStarted.Lazily
Lazily: l'upstream si avvia con il primo sottoscrittore e non si arresta mai, anche se il numero di sottoscrittori scende a zero.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val flow = flow {
println("Lazily started on first subscriber")
repeat(3) { delay(100); emit(it) }
}.shareIn(this, SharingStarted.Lazily, replay = 1)
delay(50) // no subscriber yet — not started
launch { flow.collect { println(it) } } // triggers start
delay(400)
coroutineContext.cancelChildren()
}SharingStarted.WhileSubscribed
WhileSubscribed(stopTimeout): l'upstream si avvia quando si unisce il primo sottoscrittore e si arresta dopo stopTimeout ms dall'uscita dell'ultimo. È perfetto per i ViewModel.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val flow = flow {
println("Started")
repeat(10) { delay(100); emit(it) }
}.shareIn(this, SharingStarted.WhileSubscribed(500))
val job = launch { flow.collect { print("$it ") } }
delay(300)
job.cancel() // subscriber left
delay(200) // within 500ms stop timeout — still running
launch { flow.collect { print("resume $it ") } }
delay(500)
coroutineContext.cancelChildren()
}Pattern tipico per ViewModel
Nei ViewModel Android, converta i flussi del repository con stateIn(viewModelScope, WhileSubscribed(5000), initialValue) per condividerli e mantenerli durante i cambiamenti di configurazione.
import kotlinx.coroutines.flow.*
// In ViewModel:
// val uiState: StateFlow<UiState> = repository
// .dataFlow()
// .map { UiState.Success(it) }
// .stateIn(
// scope = viewModelScope,
// started = SharingStarted.WhileSubscribed(5_000),
// initialValue = UiState.Loading
// )
fun main() { println("WhileSubscribed(5000) is the recommended ViewModel pattern") }shareIn e stateIn a confronto
shareIn → SharedFlow (nessun valore corrente, replay configurabile). stateIn → StateFlow (ha sempre un valore, replay=1). Scelga in base alla necessità dei consumer di accedere allo stato corrente.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val cold = (1..3).asFlow().map { it * 10 }
// SharedFlow — no initial value:
val shared: SharedFlow<Int> = cold.shareIn(this, SharingStarted.Eagerly, replay = 1)
// StateFlow — always has a value:
val state: StateFlow<Int> = cold.stateIn(this, SharingStarted.Eagerly, 0)
delay(100)
println("shared cache: ${shared.replayCache}")
println("state value: ${state.value}")
coroutineContext.cancelChildren()
}Compromessi della cache di replay
Un replay più elevato consente ai sottoscrittori che arrivano in ritardo di vedere una cronologia più ampia, ma richiede più memoria. Per lo stato dell'interfaccia, replay=1 (stateIn) è sufficiente. Per i log degli eventi potrebbe essere necessario un replay più elevato.
import kotlinx.coroutines.flow.*
// replay=0: no history, only future events
// replay=1: last value (equivalent to stateIn)
// replay=N: last N events — use for message feeds, logs
// Trade-off: memory vs subscriber freshness
fun main() { println("Choose replay based on late-subscriber requirements") }Rischi dei flussi non condivisi
Senza condivisione, ogni collettore Compose o osservatore del ViewModel esegue di nuovo l'upstream, causando chiamate di rete e query al database ridondanti. Condivida sempre i flussi costosi.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun expensiveApi() = flow {
println("API CALL") // without sharing: once per collector
emit("data")
}
fun main() = runBlocking {
val shared = expensiveApi().shareIn(this, SharingStarted.Lazily, replay = 1)
launch { shared.collect { } } // one API call, shared
launch { shared.collect { } } // same emission
delay(200)
coroutineContext.cancelChildren()
}Pulizia delle risorse durante la condivisione
Quando lo scope del flusso condiviso viene annullato, il flusso upstream viene annullato e vengono eseguiti i suoi blocchi finally: le risorse vengono pulite correttamente.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun resourceFlow() = flow {
try { repeat(10) { delay(100); emit(it) } }
finally { println("Upstream cleaned up") }
}
fun main() = runBlocking {
val scope = CoroutineScope(SupervisorJob())
val shared = resourceFlow().shareIn(scope, SharingStarted.Eagerly)
launch { shared.collect { print("$it ") } }
delay(250)
scope.cancel() // upstream cleanup runs
delay(100)
}Verifica rapida
Quale strategia SharingStarted è consigliata per i ViewModel Android?
Riepilogo
shareIn converte in SharedFlow, mentre stateIn converte in StateFlow. Utilizzi WhileSubscribed nei ViewModel per risparmiare risorse. La condivisione impedisce esecuzioni upstream ridondanti quando si sottoscrivono più collettori.
Domande Frequenti
La lezione «Convertire un Flow cold in hot con shareIn e stateIn» è gratuita?
Sì — il testo completo di «Convertire un Flow cold in hot con shareIn e stateIn» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Kotlin Academy, passa a CoddyKit PRO. Il corso Kotlin Academy include 4 lezioni in totale.
Cosa imparerò in «Convertire un Flow cold in hot con shareIn e stateIn»?
Trasformi Flow cold in stream hot con gli operatori di condivisione. Eserciti Kotlin Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Kotlin Academy?
Non è richiesta alcuna esperienza precedente. Kotlin Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.
Quanto tempo richiede la lezione «Convertire un Flow cold in hot con shareIn e stateIn»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Kotlin Academy?
Sì. Ogni lezione Kotlin Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- StateFlow: contenitore hot dello stato per l'interfaccia
- SharedFlow: event bus ed eventi one-shot
- Convertire un Flow cold in hot con shareIn e stateIn
- Testare StateFlow e SharedFlow con Turbine