Sobes.tech
Middle

Comment peut-on basculer entre les threads dans Flow ? Sur quel thread Flow fonctionne-t-il ?

sobes.tech IA

Réponse de l'IA

Par défaut, Flow fonctionne sur le thread à partir duquel il a été lancé. Vous pouvez changer de thread en utilisant l'opérateur flowOn.

import kotlinx.coroutines.CoroutineDispatcher // Pour CoroutineDispatcher
import kotlinx.coroutines.Dispatchers // Pour Dispatchers
import kotlinx.coroutines.flow.Flow // Pour Flow
import kotlinx.coroutines.flow.flow // Pour flow
import kotlinx.coroutines.flow.flowOn // Pour flowOn
import kotlinx.coroutines.runBlocking // Pour runBlocking

// Exemple de fonction renvoyant un Flow
fun simpleFlow(): Flow<Int> = flow {
    logThread("Flow démarré") // Journalisation du thread où le builder de flow est lancé
    for (i in 1..3) {
        emit(i) // Émission des valeurs
    }
}

fun main() = runBlocking {
    simpleFlow()
        .flowOn(Dispatchers.IO) // Change upstream (émiteurs) vers Dispatchers.IO
        .collect { value ->
            logThread("Collecté $value") // Journalisation du thread où les valeurs sont collectées
        }
}

// Fonction auxiliaire pour journaliser le thread
fun logThread(msg: String) {
    println("[$msg] [${Thread.currentThread().name}]")
}

L'opérateur flowOn influence le thread sur lequel s'exécutent les opérateurs avant lui dans la chaîne (y compris le flow builder). Les opérateurs après flowOn s'exécutent sur le thread spécifié dans l'argument flowOn. Si plusieurs flowOn sont présents dans la chaîne, chacun influence la partie de la chaîne qui le précède.

Exemple avec plusieurs flowOn:

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking

fun simpleFlowWithMultipleFlowOn(): Flow<String> = flow {
    logThread("Builder de Flow démarré") // Fonctionne sur le premier flowOn spécifié
    emit("A")
    emit("B")
}.map {
    logThread("Mapping $it") // Fonctionne sur le deuxième flowOn spécifié
    it.toLowerCase()
}.flowOn(Dispatchers.Default) // Le deuxième flowOn influence map() et le builder de flow
.filter {
    logThread("Filtrage $it") // Fonctionne sur le thread où est appelé collect (par défaut main/runBlocking)
    true
}.flowOn(Dispatchers.IO) // Le premier flowOn influence map() et le builder de flow

fun main() = runBlocking {
    simpleFlowWithMultipleFlowOn().collect { value ->
        logThread("Collecte $value") // Fonctionne sur le thread runBlocking (main)
    }
}

fun logThread(msg: String) {
    println("[$msg] [${Thread.currentThread().name}]")
}

Dans cet exemple :

  • Le flow builder et map fonctionneront sur Dispatchers.Default (car c'est le dernier flowOn dans la chaîne avant eux).
  • filter fonctionnera sur le thread depuis lequel collect est appelé, c'est-à-dire sur le thread où s'exécute runBlocking (habituellement Main en Android).

Il est important de comprendre que flowOn crée un buffer intermédiaire et ne constitue pas un "changement de thread transparent".