0Pricing
Kotlin Academy · Leçon

Transformer un Flow froid en flux actif avec shareIn et stateIn

Transformez des Flows froids en flux actifs avec des opérateurs de partage.

Transformer un Flow froid en flux actif avec shareIn et stateIn est une leçon Kotlin Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Kotlin Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Kotlin Academy comprend 4 leçons au total.

Flux froids et flux chauds

Les flux froids redémarrent pour chaque collecteur : les flux chauds partagent un seul abonnement en amont. Convertir un flux froid en flux chaud évite le travail redondant lorsque plusieurs collecteurs s’abonnent.

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

Principes de shareIn

flow.shareIn(scope, started, replay) convertit un Flow froid en SharedFlow en partageant un seul abonnement en amont.

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

Principes de stateIn

flow.stateIn(scope, started, initialValue) convertit le flux en StateFlow : celui-ci possède toujours une valeur et en rejoue une.

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 : le flux en amont démarre immédiatement lorsque shareIn/stateIn est appelé, quels que soient les abonnés.

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 : le flux en amont démarre avec le premier abonné et ne s’arrête jamais, même si le nombre d’abonnés tombe à zéro.

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) : le flux en amont démarre lorsque le premier abonné arrive et s’arrête stopTimeout millisecondes après le départ du dernier abonné. Cette stratégie convient parfaitement aux 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()
}

Schéma classique pour un ViewModel

Dans les ViewModels Android, convertissez les flux du dépôt avec stateIn(viewModelScope, WhileSubscribed(5000), initialValue) afin de les partager et de les conserver lors des changements de configuration.

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

shareIn → SharedFlow (aucune valeur courante, relecture configurable). stateIn → StateFlow (possède toujours une valeur, replay=1). Choisissez selon que les consommateurs ont besoin ou non de l’état courant.

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

Compromis du cache de relecture

Une relecture plus importante permet aux abonnés tardifs de voir davantage d’historique, mais utilise davantage de mémoire. Pour l’état de l’interface, replay=1 (stateIn) suffit. Pour les journaux d’événements, une relecture plus importante peut être nécessaire.

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

Risque d’un flux non partagé

Sans partage, chaque collecteur Compose ou observateur de ViewModel réexécute le flux en amont : cela entraîne des appels réseau et des requêtes vers la base de données redondants. Partagez toujours les flux coûteux.

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

Nettoyage des ressources lors du partage

Lorsque le scope du flux partagé est annulé, le flux en amont est annulé et ses blocs finally s’exécutent : les ressources sont correctement libérées.

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

Vérification rapide

Quelle stratégie SharingStarted est recommandée pour les ViewModels Android ?

Récapitulatif

shareIn convertit un flux en SharedFlow : stateIn le convertit en StateFlow. Utilisez WhileSubscribed dans les ViewModels pour économiser les ressources. Le partage évite les exécutions redondantes du flux en amont lorsque plusieurs collecteurs s’abonnent.

Questions Fréquemment Posées

La leçon « Transformer un Flow froid en flux actif avec shareIn et stateIn » est-elle gratuite ?

Oui — le texte complet de « Transformer un Flow froid en flux actif avec shareIn et stateIn » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Kotlin Academy, passe à CoddyKit PRO. Le cours Kotlin Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Transformer un Flow froid en flux actif avec shareIn et stateIn » ?

Transformez des Flows froids en flux actifs avec des opérateurs de partage. Tu pratiques Kotlin Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Kotlin Academy ?

Aucune expérience préalable n'est requise. Kotlin Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Transformer un Flow froid en flux actif avec shareIn et stateIn » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Kotlin Academy ?

Oui. Chaque leçon Kotlin Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. StateFlow : conteneur d’état actif pour l’interface utilisateur
  2. SharedFlow : bus d’événements et événements ponctuels
  3. Transformer un Flow froid en flux actif avec shareIn et stateIn
  4. Tester StateFlow et SharedFlow avec Turbine
← Retour à Kotlin Academy