Kotlin Flow vs StateFlow vs SharedFlow : questions d'entretien Android en 2026
Les questions Kotlin Flow vs StateFlow vs SharedFlow que posent les recruteurs Android en 2026, avec des réponses claires, un tableau comparatif et du code prêt pour la production.

Kotlin Flow vs StateFlow vs SharedFlow figure parmi les sujets d'entretien Android les plus fréquents en 2026, et confondre ces trois types reste le moyen le plus rapide de rater une session sur les coroutines. StateFlow et SharedFlow sont tous deux des flux chauds construits au-dessus de Flow, mais ils répondent à des besoins distincts : conserver un état d'un côté, diffuser des événements de l'autre. Les questions ci-dessous sont celles que les recruteurs Android posent réellement, accompagnées de réponses précises et de code prêt pour la production.
Un Flow est froid et exécute son producteur une fois par collecteur. StateFlow est un flux chaud et conflaté qui conserve toujours une valeur courante unique, idéal pour l'état de l'UI. SharedFlow est un flux chaud sans valeur initiale obligatoire, idéal pour les événements ponctuels comme une navigation ou un snackbar.
Kotlin Flow vs StateFlow vs SharedFlow : les différences fondamentales
StateFlow et SharedFlow sont des spécialisations de SharedFlow, lui-même un Flow chaud. Ce que les recruteurs cherchent à sonder, c'est la distinction froid contre chaud, la présence ou non d'une valeur courante retenue, et le comportement de chacun face aux émissions dupliquées. Un modèle mental concis : Flow est une recette, StateFlow une valeur mutable unique, et SharedFlow un bus d'événements.
| Propriété | Flow (froid) | StateFlow | SharedFlow |
|---|---|---|---|
| Température | Froid | Chaud | Chaud |
| Valeur initiale | Aucune | Obligatoire | Optionnelle (via replay) |
| Conserve la valeur courante | Non | Oui, via .value | Non |
| Émet les doublons | Oui | Non (conflaté + dédupliqué) | Configurable |
| Collecteurs supplémentaires | Nouveau flux à chaque fois | Partagé | Partagé |
| Idéal pour | Pipelines de données asynchrones | État de l'UI | Événements ponctuels |
La documentation officielle des coroutines Kotlin traite ce comportement froid par défaut comme la propriété qui définit Flow, et une réponse solide part donc de là.
Pourquoi un Flow Kotlin est-il froid par défaut ?
Un flux froid ne fait rien tant que collect() n'est pas appelé, et il réexécute son bloc producteur pour chaque collecteur. Deux collecteurs sur le même flux froid déclenchent deux appels réseau indépendants. C'est le concept le plus testé, aussi un court exemple ancre la réponse.
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
}Comme le producteur redémarre à chaque collecteur, les flux froids constituent le bon choix par défaut pour les pipelines de données à exécuter à la demande. Ils ne posent problème que lorsqu'une valeur doit être partagée à l'échelle d'un écran entier, ce qui est précisément le rôle de StateFlow et SharedFlow.
Questions d'entretien sur StateFlow : un état qui émet toujours
StateFlow est un flux chaud qui possède toujours une valeur et rejoue cette dernière valeur à chaque nouveau collecteur. Il exige une valeur initiale, expose .value pour des lectures synchrones et conflate les émissions : des mises à jour successives et rapides peuvent sauter des valeurs intermédiaires, et affecter deux fois la même valeur n'émet rien puisqu'il déduplique par égalité. Cela en fait le remplaçant moderne de LiveData dans 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) }
}
}
}Une relance fréquente : pourquoi exposer asStateFlow() plutôt que le champ mutable ? Cela offre à l'UI une vue en lecture seule, si bien que l'état ne peut changer qu'à travers le ViewModel, ce qui préserve le flux de données unidirectionnel. Le guide StateFlow et SharedFlow d'Android recommande exactement ce pattern.
SharedFlow vs StateFlow : choisir le bon type pour les événements
La question piège classique est « StateFlow peut-il livrer des événements ponctuels ? ». La réponse honnête est non, pas de façon fiable. Parce que StateFlow conflate et déduplique, un événement de navigation peut être perdu sous des mises à jour rapides, et il se redéclenche lors d'un changement de configuration car un nouveau collecteur rejoue la valeur retenue. SharedFlow avec replay = 0 corrige les deux : rien n'est retenu, et chaque émission n'atteint que les collecteurs actifs au moment de l'émission.
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)
}
}
}Pour les questions d'architecture, le raisonnement de fond derrière la séparation entre état et événements ressort dans la comparaison MVVM vs MVI, où MVI traite les événements comme un flux explicite plutôt que comme un état mutable.
Convertir un Flow froid en StateFlow chaud avec stateIn
Les recruteurs demandent souvent comment transformer le flux froid d'un repository en état d'UI. La réponse est stateIn (pour une valeur unique retenue) ou shareIn (pour une diffusion sans valeur courante). Le paramètre SharingStarted contrôle le moment où l'amont est actif, et WhileSubscribed(5_000) est le choix standard car il maintient le flux vivant pendant une rotation sans le laisser fuir à la fermeture de l'écran.
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 différence entre stateIn et shareIn est exactement celle entre StateFlow et SharedFlow : stateIn réclame une initialValue et conserve un état, tandis que shareIn prend un compteur replay et diffuse. Revoir le modèle plus large des coroutines dans ce guide pour maîtriser les coroutines Kotlin aide à relier ces opérateurs aux scopes et à l'annulation.
Prêt à réussir tes entretiens Android ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Collecter des flux en sécurité sur le cycle de vie Android
Une question de niveau senior porte sur la façon de collecter un flux sans laisser fuir du travail pendant que l'écran est en arrière-plan. Collecter à l'intérieur d'un simple launch continue de tourner quand l'UI est arrêtée, ce qui gaspille du CPU et risque des crashs. repeatOnLifecycle(STARTED) annule la collecte au STOP et la relance au 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) }
}
}Dans Jetpack Compose, l'équivalent est collectAsStateWithLifecycle(), qui cesse de collecter quand l'application passe en arrière-plan et reprend au retour.
@Composable
fun ProfileScreen(viewModel: ProfileViewModel) {
// collectAsStateWithLifecycle stops collecting when the app is backgrounded.
val state by viewModel.uiState.collectAsStateWithLifecycle()
ProfileContent(state)
}Cette conscience du cycle de vie explique pourquoi StateFlow associé à collectAsStateWithLifecycle est le pattern d'état par défaut dans les applications Compose modernes, un point approfondi dans ces questions d'entretien Jetpack Compose.
Comment tester les émissions de StateFlow et SharedFlow
Le test est le terrain où les candidats seniors se distinguent. L'approche naïve lit stateFlow.value une seule fois, mais elle rate les états intermédiaires et ne permet pas d'observer un SharedFlow. La réponse standard cite la bibliothèque Turbine, qui suspend jusqu'à l'arrivée de chaque émission et fait échouer le test si une valeur attendue ne vient jamais.
@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()
}
}Associer Turbine à runTest et à un test dispatcher garde les assertions déterministes, et mentionner StandardTestDispatcher face à UnconfinedTestDispatcher signale une vraie expérience du test de coroutines.
Relances courantes en entretien sur Kotlin Flow
Les recruteurs terminent par des vérifications à la volée. Que fait SharingStarted.WhileSubscribed(5_000) ? Il démarre l'amont au premier abonné et l'arrête 5 secondes après le désabonnement du dernier, un délai assez long pour survivre à une rotation. Pourquoi StateFlow ignore-t-il les valeurs dupliquées ? Il compare avec equals(), donc émettre une valeur égale ne produit aucun effet, ce qui explique pourquoi les data classes comptent pour l'état. SharedFlow peut-il se comporter comme StateFlow ? Fixer replay = 1 lui fait retenir la dernière valeur, mais il lui manque toujours un .value synchrone et il ne déduplique jamais. Qu'est-ce qui contrôle la contre-pression sur un SharedFlow ? Les paramètres extraBufferCapacity et onBufferOverflow, avec BufferOverflow.DROP_OLDEST comme choix courant pour les événements. Un entraînement plus poussé sur ces opérateurs se trouve dans le module d'entretien sur les coroutines et Flow Kotlin.
Utiliser StateFlow pour des événements de navigation ou de snackbar. Comme il rejoue sa dernière valeur aux nouveaux collecteurs, l'événement se redéclenche après une rotation et l'utilisateur est redirigé deux fois. Il faut recourir à SharedFlow avec replay = 0, ou modéliser un événement ponctuel effacé après traitement.
Le code source de kotlinx.coroutines et sa référence du package flow sur GitHub méritent un survol avant un entretien, car la KDoc de StateFlow et SharedFlow énonce précisément les garanties de conflation et de replay.
Conclusion
La distinction Kotlin Flow vs StateFlow vs SharedFlow récompense un langage précis en entretien. Les points clés à emporter :
Flowest froid : le producteur se réexécute pour chaque collecteur, ce qui le destine aux pipelines de données à la demande.StateFlowest chaud, conserve toujours une valeur, conflate et déduplique, ce qui en fait l'outil pour l'état de l'UI.SharedFlowest chaud sans valeur initiale obligatoire, ce qui en fait le choix correct pour les événements ponctuels.- Convertir de froid à chaud avec
stateInoushareIn, et employerSharingStarted.WhileSubscribed(5_000)pour survivre à une rotation. - Collecter avec
repeatOnLifecycle(STARTED)oucollectAsStateWithLifecycle()pour éviter le travail en arrière-plan et les fuites. - Ne jamais livrer d'événements via StateFlow : son comportement de replay les redéclenche après un changement de configuration.
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
Tags
Partager
Articles similaires

Kotlin 2.3 pour Android : Destructuration par Nom, KMP et Questions d'Entretien 2026
Questions d'entretien Kotlin 2.3 pour développeurs Android en 2026. Destructuration par nom, KMP, paramètres de contexte, Flow et coroutines avec exemples de code.

Top 20 questions d'entretien Jetpack Compose en 2026
Les 20 questions les plus posées en entretien sur Jetpack Compose : recomposition, state, navigation, performance et architecture.

Maîtriser les Coroutines Kotlin : Guide complet 2026
Apprenez à maîtriser les coroutines Kotlin pour le développement Android : suspend functions, scopes, dispatchers et patterns avancés.