Kotlin Flow vs StateFlow vs SharedFlow: preguntas de entrevista de Android en 2026

Las preguntas sobre Kotlin Flow vs StateFlow vs SharedFlow que hacen los entrevistadores de Android en 2026, con respuestas claras, una tabla comparativa y código listo para producción.

Comparación entre Kotlin Flow, StateFlow y SharedFlow para entrevistas de Android

Kotlin Flow vs StateFlow vs SharedFlow es uno de los temas más frecuentes en las entrevistas de Android en 2026, y confundir los tres es la forma más rápida de perder una ronda de coroutines. StateFlow y SharedFlow son ambos streams calientes construidos sobre Flow, pero existen para resolver problemas distintos: mantener estado frente a difundir eventos. Las preguntas que siguen son las que los entrevistadores de Android hacen de verdad, con respuestas precisas y código listo para producción.

La respuesta en 20 segundos

Un Flow es frío y ejecuta su productor una vez por cada colector. StateFlow es un stream caliente y conflado que siempre mantiene un único valor actual, ideal para el estado de la UI. SharedFlow es un stream caliente sin valor inicial obligatorio, ideal para eventos únicos como una navegación o un snackbar.

Kotlin Flow vs StateFlow vs SharedFlow: las diferencias fundamentales

StateFlow y SharedFlow son especializaciones de SharedFlow, que a su vez es un Flow caliente. La distinción que los entrevistadores exploran es frío frente a caliente, si se retiene un valor actual y cómo se comporta cada uno ante emisiones duplicadas. Un modelo mental conciso: Flow es una receta, StateFlow es un único valor mutable y SharedFlow es un bus de eventos.

| Propiedad | Flow (frío) | StateFlow | SharedFlow | |---|---|---|---| | Temperatura | Frío | Caliente | Caliente | | Valor inicial | Ninguno | Obligatorio | Opcional (vía replay) | | Mantiene valor actual | No | Sí, vía .value | No | | Emite duplicados | Sí | No (conflado + deduplicado) | Configurable | | Colectores adicionales | Un stream nuevo cada vez | Compartido | Compartido | | Ideal para | Pipelines de datos asíncronos | Estado de la UI | Eventos únicos |

La documentación oficial de coroutines de Kotlin considera este comportamiento frío por defecto como la propiedad que define a Flow, así que una buena respuesta parte de ahí.

¿Por qué un Kotlin Flow es frío por defecto?

Un flow frío no hace nada hasta que se llama a collect(), y vuelve a ejecutar su bloque productor para cada colector. Dos colectores sobre el mismo flow frío disparan dos llamadas de red independientes. Este es el concepto que más se evalúa, así que un ejemplo breve ancla la respuesta.

SearchRepository.ktkotlin
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
}

Como el productor se reinicia por cada colector, los flows fríos son el valor por defecto correcto para pipelines de datos que deben ejecutarse bajo demanda. Solo se vuelven un problema cuando un valor debe compartirse en toda una pantalla, que es justo donde entran StateFlow y SharedFlow.

Preguntas de entrevista sobre StateFlow: estado que siempre emite

StateFlow es un flow caliente que siempre tiene un valor y reenvía ese último valor a cada nuevo colector. Requiere un valor inicial, expone .value para lecturas síncronas y confla las emisiones: actualizaciones rápidas y sucesivas pueden saltarse valores intermedios, y asignar el mismo valor dos veces no emite nada porque deduplica por igualdad. Esto lo convierte en el reemplazo moderno de LiveData dentro de un ViewModel.

SearchViewModel.ktkotlin
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) }
        }
    }
}

Una repregunta habitual: ¿por qué exponer asStateFlow() en lugar del campo mutable? Le entrega a la UI una vista de solo lectura, de modo que el estado solo puede cambiar a través del ViewModel, lo que mantiene intacto el flujo de datos unidireccional. La propia guía de StateFlow y SharedFlow de Android recomienda exactamente este patrón.

SharedFlow vs StateFlow: elegir el tipo correcto para eventos

La pregunta trampa clásica es: «¿puede StateFlow entregar eventos únicos?». La respuesta honesta es no, no de forma segura. Como StateFlow confla y deduplica, un evento de navegación puede perderse ante actualizaciones rápidas, y se vuelve a disparar en un cambio de configuración porque un nuevo colector reproduce el valor retenido. SharedFlow con replay = 0 resuelve ambos casos: no se retiene nada y cada emisión llega únicamente a los colectores activos en el momento de emitir.

CheckoutViewModel.ktkotlin
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)
        }
    }
}

Para preguntas de arquitectura, el razonamiento más profundo detrás de separar el estado de los eventos aparece en la comparación entre MVVM y MVI, donde MVI trata los eventos como un stream explícito en lugar de estado mutable.

Convertir un Flow frío en un StateFlow caliente con stateIn

Los entrevistadores preguntan con frecuencia cómo transformar el flow frío de un repositorio en estado de UI. La respuesta es stateIn (para un único valor retenido) o shareIn (para una difusión sin valor actual). El parámetro SharingStarted controla cuándo está activo el upstream, y WhileSubscribed(5_000) es la opción estándar porque mantiene vivo el flow durante una rotación sin filtrarlo cuando la pantalla se cierra.

ProfileViewModel.ktkotlin
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 diferencia entre stateIn y shareIn es exactamente la diferencia entre StateFlow y SharedFlow: stateIn necesita un initialValue y mantiene estado, mientras que shareIn recibe un conteo de replay y difunde. Repasar el modelo de coroutines en general en esta guía para dominar las coroutines de Kotlin ayuda a conectar estos operadores con los scopes y la cancelación.

¿Listo para aprobar tus entrevistas de Android?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Recolectar flows de forma segura en el ciclo de vida de Android

Una pregunta de nivel senior es cómo recolectar un flow sin filtrar trabajo mientras la pantalla está en segundo plano. Recolectar dentro de un launch simple sigue ejecutándose cuando la UI está detenida, desperdiciando CPU y arriesgando crashes. repeatOnLifecycle(STARTED) cancela la recolección en STOP y la reinicia en START.

ProfileFragment.kt (View system)kotlin
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) }
    }
}

En Jetpack Compose el equivalente es collectAsStateWithLifecycle(), que deja de recolectar cuando la app pasa a segundo plano y se reanuda al volver.

ProfileScreen.kt (Jetpack Compose)kotlin
@Composable
fun ProfileScreen(viewModel: ProfileViewModel) {
    // collectAsStateWithLifecycle stops collecting when the app is backgrounded.
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    ProfileContent(state)
}

Esta conciencia del ciclo de vida es la razón por la que StateFlow junto con collectAsStateWithLifecycle es el patrón de estado por defecto en las apps modernas con Compose, un punto que se profundiza en estas preguntas de entrevista sobre Jetpack Compose.

Cómo probar las emisiones de StateFlow y SharedFlow

Las pruebas son donde los candidatos senior marcan la diferencia. El enfoque ingenuo lee stateFlow.value una sola vez, pero eso omite los estados intermedios y no puede observar un SharedFlow en absoluto. La respuesta estándar menciona la librería Turbine, que suspende hasta que llega cada emisión y hace fallar la prueba si un valor esperado nunca aparece.

SearchViewModelTest.ktkotlin
@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()
    }
}

Combinar Turbine con runTest y un dispatcher de prueba mantiene las aserciones deterministas, y mencionar StandardTestDispatcher frente a UnconfinedTestDispatcher demuestra experiencia real probando coroutines.

Repreguntas frecuentes en entrevistas sobre Kotlin Flow

Los entrevistadores cierran con preguntas rápidas. ¿Qué hace SharingStarted.WhileSubscribed(5_000)? Inicia el upstream con el primer suscriptor y lo detiene 5 segundos después de que el último se da de baja, tiempo suficiente para sobrevivir a una rotación. ¿Por qué StateFlow omite valores duplicados? Compara con equals(), así que emitir un valor igual no tiene efecto, y por eso las data classes importan para el estado. ¿Puede SharedFlow comportarse como StateFlow? Poner replay = 1 hace que retenga el último valor, pero sigue sin tener un .value síncrono y nunca deduplica. ¿Qué controla la contrapresión en un SharedFlow? Los parámetros extraBufferCapacity y onBufferOverflow, siendo BufferOverflow.DROP_OLDEST una opción habitual para eventos. Más práctica sobre estos operadores está en el módulo de entrevista sobre coroutines y Flow de Kotlin.

El error que reprueba a los candidatos

Usar StateFlow para eventos de navegación o snackbar. Como reenvía su último valor a los nuevos colectores, el evento se vuelve a disparar tras una rotación y el usuario navega dos veces. Conviene recurrir a SharedFlow con replay = 0, o modelar un evento de una sola vez que se limpia después de manejarlo.

Vale la pena echar un vistazo al código fuente de kotlinx.coroutines y a su referencia del paquete flow en GitHub antes de una entrevista, ya que el KDoc de StateFlow y SharedFlow enuncia con precisión las garantías de conflación y replay.

Conclusión

La distinción entre Kotlin Flow vs StateFlow vs SharedFlow premia el lenguaje preciso en una entrevista. Ideas clave para llevar:

  • Flow es frío: el productor se vuelve a ejecutar para cada colector, por lo que encaja en pipelines de datos bajo demanda.
  • StateFlow es caliente, siempre mantiene un valor y confla y deduplica, lo que lo convierte en la herramienta para el estado de la UI.
  • SharedFlow es caliente y no exige valor inicial, lo que lo hace la opción correcta para eventos únicos.
  • La conversión de frío a caliente se hace con stateIn o shareIn, y SharingStarted.WhileSubscribed(5_000) permite sobrevivir a una rotación.
  • Recolectar con repeatOnLifecycle(STARTED) o collectAsStateWithLifecycle() evita el trabajo en segundo plano y las fugas.
  • Nunca conviene entregar eventos a través de StateFlow: su comportamiento de replay los vuelve a disparar tras un cambio de configuración.

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Etiquetas

#android
#kotlin
#coroutines
#flow
#interview

Compartir

Artículos relacionados