Kotlin Flow vs StateFlow vs SharedFlow: domande da colloquio Android nel 2026
Le domande su Kotlin Flow vs StateFlow vs SharedFlow che gli intervistatori Android pongono nel 2026, con risposte chiare, una tabella comparativa e codice pronto per la produzione.

Kotlin Flow vs StateFlow vs SharedFlow è uno degli argomenti più frequenti nei colloqui Android del 2026, e confondere i tre tipi è il modo più rapido per fallire una sessione sulle coroutine. StateFlow e SharedFlow sono entrambi stream hot costruiti sopra Flow, ma esistono per risolvere problemi diversi: mantenere lo stato oppure trasmettere eventi. Le domande che seguono sono quelle che gli intervistatori Android pongono davvero, con risposte precise e codice pronto per la produzione.
Un Flow è cold ed esegue il suo produttore una volta per ogni collector. StateFlow è uno stream hot e conflated che mantiene sempre un unico valore corrente, ideale per lo stato della UI. SharedFlow è uno stream hot senza valore iniziale obbligatorio, ideale per eventi una tantum come la navigazione o una snackbar.
Kotlin Flow vs StateFlow vs SharedFlow: le differenze fondamentali
StateFlow e SharedFlow sono specializzazioni di SharedFlow, che a sua volta è un Flow hot. La distinzione su cui insistono gli intervistatori riguarda cold contro hot, se viene conservato un valore corrente e come ciascun tipo si comporta con le emissioni duplicate. Un modello mentale conciso: Flow è una ricetta, StateFlow è un singolo valore mutabile e SharedFlow è un event bus.
| Proprietà | Flow (cold) | StateFlow | SharedFlow |
|---|---|---|---|
| Temperatura | Cold | Hot | Hot |
| Valore iniziale | Nessuno | Obbligatorio | Opzionale (tramite replay) |
| Mantiene il valore corrente | No | Sì, tramite .value | No |
| Emette duplicati | Sì | No (conflated + deduplicato) | Configurabile |
| Collector aggiuntivi | Nuovo stream ogni volta | Condiviso | Condiviso |
| Ideale per | Pipeline di dati asincroni | Stato della UI | Eventi una tantum |
La documentazione ufficiale delle coroutine Kotlin considera questo comportamento cold-by-default come la proprietà distintiva di Flow, quindi una buona risposta parte da qui.
Perché un Kotlin Flow è cold per impostazione predefinita?
Un flow cold non fa nulla finché non viene chiamato collect(), e riesegue il proprio blocco produttore per ogni collector. Due collector sullo stesso flow cold scatenano due chiamate di rete indipendenti. È il concetto testato più di ogni altro, quindi un breve esempio ancora la risposta.
fun searchResults(query: String): Flow<List<Result>> = flow {
// This block runs fresh for every collector.
// Nothing executes until a collector calls collect().
val results = api.search(query) // suspending network call
emit(results) // pushed downstream to the collector
}Poiché il produttore riparte per ogni collector, i flow cold sono il default corretto per le pipeline di dati che devono essere eseguite su richiesta. Diventano un problema solo quando un valore deve essere condiviso su un'intera schermata, ed è esattamente qui che entrano in gioco StateFlow e SharedFlow.
Domande da colloquio su StateFlow: uno stato che emette sempre
StateFlow è un flow hot che ha sempre un valore e ripropone quest'ultimo valore a ogni nuovo collector. Richiede un valore iniziale, espone .value per le letture sincrone e conflaziona le emissioni: aggiornamenti rapidi in successione possono saltare i valori intermedi, e impostare due volte lo stesso valore non emette nulla perché la deduplicazione avviene tramite uguaglianza. Questo lo rende il sostituto moderno di LiveData all'interno di un ViewModel.
class SearchViewModel(private val repo: SearchRepository) : ViewModel() {
// MutableStateFlow demands an initial value, so the UI always has something to render.
private val _uiState = MutableStateFlow<SearchUiState>(SearchUiState.Idle)
val uiState: StateFlow<SearchUiState> = _uiState.asStateFlow()
fun onQueryChanged(query: String) {
_uiState.value = SearchUiState.Loading
viewModelScope.launch {
val results = repo.search(query)
// update {} applies the change atomically, safe under concurrent callers.
_uiState.update { SearchUiState.Success(results) }
}
}
}Un follow-up frequente: perché esporre asStateFlow() invece del campo mutabile? Perché consegna alla UI una vista in sola lettura, così lo stato può cambiare solo attraverso il ViewModel, mantenendo intatto il flusso di dati unidirezionale. La stessa guida ad StateFlow e SharedFlow di Android consiglia esattamente questo pattern.
SharedFlow vs StateFlow: scegliere il tipo giusto per gli eventi
La classica domanda trabocchetto è: «StateFlow può consegnare eventi una tantum?». La risposta onesta è no, non in modo sicuro. Poiché StateFlow conflaziona e deduplica, un evento di navigazione può andare perso durante aggiornamenti rapidi, e viene rilanciato al cambio di configurazione perché un nuovo collector ripropone il valore conservato. SharedFlow con replay = 0 risolve entrambi i problemi: non viene conservato nulla e ogni emissione raggiunge solo i collector attivi nel momento dell'emissione.
class CheckoutViewModel : ViewModel() {
// replay = 0: a late collector must NOT re-receive a past navigation event.
private val _events = MutableSharedFlow<CheckoutEvent>(replay = 0)
val events: SharedFlow<CheckoutEvent> = _events.asSharedFlow()
fun onPaymentConfirmed() {
viewModelScope.launch {
// emit() suspends if the buffer is full; tryEmit() is the non-suspending variant.
_events.emit(CheckoutEvent.NavigateToReceipt)
}
}
}Per le domande sull'architettura, il ragionamento più profondo dietro la separazione tra stato ed eventi emerge nel confronto tra MVVM e MVI, dove MVI tratta gli eventi come uno stream esplicito anziché come stato mutabile.
Convertire un Flow cold in uno StateFlow hot con stateIn
Gli intervistatori chiedono spesso come trasformare il flow cold di un repository in stato della UI. La risposta è stateIn (per un singolo valore conservato) oppure shareIn (per una trasmissione senza valore corrente). Il parametro SharingStarted controlla quando l'upstream è attivo, e WhileSubscribed(5_000) è la scelta standard perché mantiene il flow vivo durante una rotazione senza generare leak quando la schermata si chiude.
val profile: StateFlow<Profile> = repo.profileStream()
.stateIn(
scope = viewModelScope,
// Keep the upstream alive 5s after the last collector leaves, surviving rotation.
started = SharingStarted.WhileSubscribed(5_000),
initialValue = Profile.EMPTY
)La differenza tra stateIn e shareIn è esattamente la differenza tra StateFlow e SharedFlow: stateIn richiede un initialValue e mantiene lo stato, mentre shareIn accetta un conteggio replay e trasmette. Rivedere il modello più ampio delle coroutine in questa guida per padroneggiare le coroutine Kotlin aiuta a collegare questi operatori agli scope e alla cancellazione.
Pronto a superare i tuoi colloqui su Android?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Raccogliere i flow in sicurezza nel ciclo di vita Android
Una domanda di livello senior è come raccogliere un flow senza sprecare lavoro mentre la schermata è in background. Raccogliere all'interno di un semplice launch continua a girare quando la UI è ferma, sprecando CPU e rischiando crash. repeatOnLifecycle(STARTED) annulla la raccolta su STOP e la riavvia su START.
viewLifecycleOwner.lifecycleScope.launch {
// Collection restarts on STARTED and cancels on STOPPED: no wasted work in the background.
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state -> render(state) }
}
}In Jetpack Compose l'equivalente è collectAsStateWithLifecycle(), che interrompe la raccolta quando l'app passa in background e la riprende al ritorno.
@Composable
fun ProfileScreen(viewModel: ProfileViewModel) {
// collectAsStateWithLifecycle stops collecting when the app is backgrounded.
val state by viewModel.uiState.collectAsStateWithLifecycle()
ProfileContent(state)
}Questa consapevolezza del ciclo di vita è il motivo per cui StateFlow abbinato a collectAsStateWithLifecycle è il pattern di stato predefinito nelle app Compose moderne, un aspetto approfondito in queste domande da colloquio su Jetpack Compose.
Come testare le emissioni di StateFlow e SharedFlow
Il testing è ciò che distingue i candidati senior. L'approccio ingenuo legge stateFlow.value una sola volta, ma così si perdono gli stati intermedi e non è possibile osservare affatto un SharedFlow. La risposta standard cita la libreria Turbine, che si sospende finché ogni emissione non arriva e fa fallire il test se un valore atteso non arriva mai.
@Test
fun `emits Loading then Success on query`() = runTest {
val viewModel = SearchViewModel(fakeRepo)
viewModel.uiState.test {
assertEquals(SearchUiState.Idle, awaitItem()) // initial value
viewModel.onQueryChanged("kotlin")
assertEquals(SearchUiState.Loading, awaitItem()) // intermediate state
assertEquals(SearchUiState.Success(fakeResults), awaitItem())
cancelAndIgnoreRemainingEvents()
}
}Abbinare Turbine a runTest e a un test dispatcher mantiene deterministiche le asserzioni, e menzionare StandardTestDispatcher rispetto a UnconfinedTestDispatcher segnala una reale esperienza nel testing delle coroutine.
Domande di approfondimento frequenti sui Kotlin Flow
Gli intervistatori chiudono con una raffica di verifiche rapide. Cosa fa SharingStarted.WhileSubscribed(5_000)? Avvia l'upstream al primo subscriber e lo ferma 5 secondi dopo che l'ultimo si è disiscritto, un tempo sufficiente a sopravvivere a una rotazione. Perché StateFlow salta i valori duplicati? Confronta con equals(), quindi emettere un valore uguale è un no-op, ed è per questo che le data class contano per lo stato. SharedFlow può comportarsi come StateFlow? Impostare replay = 1 gli fa conservare l'ultimo valore, ma continua a non avere un .value sincrono e non deduplica mai. Cosa controlla la back-pressure su un SharedFlow? I parametri extraBufferCapacity e onBufferOverflow, con BufferOverflow.DROP_OLDEST come scelta comune per gli eventi. Una pratica più approfondita su questi operatori si trova nel modulo di colloquio su coroutine e Flow in Kotlin.
Usare StateFlow per eventi di navigazione o snackbar. Poiché ripropone il suo ultimo valore ai nuovi collector, l'evento viene rilanciato dopo una rotazione e l'utente viene navigato due volte. Meglio ricorrere a SharedFlow con replay = 0, oppure modellare un evento one-shot che viene azzerato dopo essere stato gestito.
Vale la pena dare un'occhiata al codice sorgente di kotlinx.coroutines e al suo riferimento del package flow su GitHub prima di un colloquio, poiché la KDoc su StateFlow e SharedFlow enuncia con precisione le garanzie di conflation e replay.
Conclusione
La distinzione tra Kotlin Flow, StateFlow e SharedFlow premia il linguaggio preciso durante un colloquio. I punti chiave da portare con sé:
Flowè cold: il produttore viene rieseguito per ogni collector, quindi si adatta alle pipeline di dati su richiesta.StateFlowè hot, mantiene sempre un valore, conflaziona e deduplica, il che lo rende lo strumento per lo stato della UI.SharedFlowè hot e non richiede un valore iniziale, il che lo rende la scelta corretta per gli eventi una tantum.- Convertire da cold a hot con
stateInoshareIn, e usareSharingStarted.WhileSubscribed(5_000)per sopravvivere alla rotazione. - Raccogliere con
repeatOnLifecycle(STARTED)ocollectAsStateWithLifecycle()per evitare lavoro in background e leak. - Non consegnare mai gli eventi tramite StateFlow: il suo comportamento di replay li rilancia dopo un cambio di configurazione.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Tag
Condividi
Articoli correlati

Kotlin 2.3 per Android: Destrutturazione Basata sui Nomi, KMP e Domande da Colloquio 2026
Domande da colloquio su Kotlin 2.3 riguardanti la destrutturazione basata sui nomi, Kotlin Multiplatform, parametri di contesto, coroutine e Flow. Preparazione ai colloqui per sviluppatori Android nel 2026 con esempi di codice reali.

Le 20 domande più frequenti su Jetpack Compose nei colloqui 2026
Le 20 domande più frequenti su Jetpack Compose nei colloqui tecnici: recomposition, gestione dello stato, navigazione, performance e pattern architetturali.

Kotlin Coroutines per Android: Guida Completa 2026
Guida approfondita alle coroutine Kotlin per lo sviluppo Android: funzioni suspend, scope, dispatcher, Flow e pattern avanzati.