0Pricing
Kotlin Academy · Aula

Convertendo Flow frio em ativo com shareIn e stateIn

Transforme Flows frios em fluxos ativos com operadores de compartilhamento.

Convertendo Flow frio em ativo com shareIn e stateIn é uma aula grátis de Kotlin Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Kotlin Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Kotlin Academy inclui 4 aulas no total.

Fluxos frios e quentes

Fluxos frios são reiniciados para cada coletor; fluxos quentes compartilham uma única assinatura do fluxo de origem. Converter um fluxo frio em quente evita trabalho redundante quando vários coletores se inscrevem.

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()
}

Noções básicas de shareIn

flow.shareIn(scope, started, replay) converte um Flow frio em um SharedFlow, compartilhando uma única assinatura do fluxo de origem.

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()
}

Noções básicas de stateIn

flow.stateIn(scope, started, initialValue) converte o fluxo em um StateFlow — que sempre tem um valor e repete 1 valor.

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: o fluxo de origem é iniciado imediatamente quando shareIn/stateIn é chamado, independentemente dos assinantes.

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: o fluxo de origem é iniciado com o primeiro assinante e nunca é interrompido, mesmo quando o número de assinantes chega 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): o fluxo de origem é iniciado quando o primeiro assinante entra e é interrompido stopTimeout milissegundos depois que o último assinante sai. É perfeito para ViewModels.

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()
}

Padrão típico de ViewModel

Em ViewModels do Android, converta os fluxos do repositório com stateIn(viewModelScope, WhileSubscribed(5000), initialValue) para compartilhá-los e mantê-los durante alterações de configuração.

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 versus stateIn

shareIn → SharedFlow (sem valor atual e com repetição configurável). stateIn → StateFlow (sempre tem um valor e repete 1 valor). Escolha com base na necessidade de os consumidores terem um estado atual.

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()
}

Compromissos do cache de repetição

Uma repetição maior permite que assinantes tardios vejam mais histórico, mas usa mais memória. Para o estado da interface, replay=1 (stateIn) é suficiente. Para registros de eventos, talvez seja necessária uma repetição maior.

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") }

Risco de fluxo não compartilhado

Sem compartilhamento, cada coletor do Compose ou observador do ViewModel executa novamente o fluxo de origem — fazendo chamadas de rede e consultas ao banco de dados redundantes. Sempre compartilhe fluxos caros.

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()
}

Limpeza de recursos durante o compartilhamento

Quando o escopo do fluxo compartilhado é cancelado, o fluxo de origem é cancelado e seus blocos finally são executados — os recursos são liberados corretamente.

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ção rápida

Qual estratégia de SharingStarted é recomendada para ViewModels do Android?

Recapitulação

shareIn converte para SharedFlow; stateIn converte para StateFlow. Use WhileSubscribed em ViewModels para economizar recursos. O compartilhamento evita execuções redundantes do fluxo de origem quando vários coletores se inscrevem.

Perguntas Frequentes

A aula “Convertendo Flow frio em ativo com shareIn e stateIn” é grátis?

Sim — o texto completo de “Convertendo Flow frio em ativo com shareIn e stateIn” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Kotlin Academy, atualize para CoddyKit PRO. O curso de Kotlin Academy inclui 4 aulas no total.

O que vou aprender em “Convertendo Flow frio em ativo com shareIn e stateIn”?

Transforme Flows frios em fluxos ativos com operadores de compartilhamento. Você pratica Kotlin Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Kotlin Academy?

Nenhuma experiência prévia é necessária. Kotlin Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Convertendo Flow frio em ativo com shareIn e stateIn”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Kotlin Academy?

Sim. Cada aula de Kotlin Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. StateFlow: contêiner de estado ativo para a interface
  2. SharedFlow: barramentos de eventos e eventos de disparo único
  3. Convertendo Flow frio em ativo com shareIn e stateIn
  4. Testando StateFlow e SharedFlow com Turbine
← Voltar para Kotlin Academy