# 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. - Published: 2026-07-01 - Updated: 2026-07-07 - Author: SharpSkill - Tags: android, kotlin, coroutines, flow, interview - Reading time: 9 min --- 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. > **La risposta in 20 secondi** > > 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](https://kotlinlang.org/docs/flow.html) 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. ```kotlin // SearchRepository.kt fun searchResults(query: String): Flow> = 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`. ```kotlin // SearchViewModel.kt 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.Idle) val uiState: StateFlow = _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](https://developer.android.com/kotlin/flow/stateflow-and-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. ```kotlin // CheckoutViewModel.kt class CheckoutViewModel : ViewModel() { // replay = 0: a late collector must NOT re-receive a past navigation event. private val _events = MutableSharedFlow(replay = 0) val events: SharedFlow = _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](/blog/android/mvvm-vs-mvi-architecture), 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. ```kotlin // ProfileViewModel.kt val profile: StateFlow = 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](/blog/android/mastering-kotlin-coroutines) aiuta a collegare questi operatori agli scope e alla cancellazione. ## 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`. ```kotlin // ProfileFragment.kt (View system) 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. ```kotlin // ProfileScreen.kt (Jetpack Compose) @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](/blog/android/jetpack-compose-interview-questions). ## 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. ```kotlin // SearchViewModelTest.kt @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](/technologies/android/interview-questions/android-kotlin-coroutines-flow). > **L'errore che fa bocciare i candidati** > > 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](https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-core) 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 `stateIn` o `shareIn`, e usare `SharingStarted.WhileSubscribed(5_000)` per sopravvivere alla rotazione. - Raccogliere con `repeatOnLifecycle(STARTED)` o `collectAsStateWithLifecycle()` 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. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/android/kotlin-flow-vs-stateflow-vs-sharedflow-interview