Sobes.tech
Middle

Hogyan lehet váltani a szálakat a Flow-ban? Melyik szálon fut a Flow?

sobes.tech MI

Válasz az MI-től

Alapértelmezés szerint a Flow azon a szálon fut, ahonnan elindították. A flowOn operátor segítségével szálat lehet váltani.

import kotlinx.coroutines.CoroutineDispatcher // A CoroutineDispatcher-hez
import kotlinx.coroutines.Dispatchers // A Dispatchers-hez
import kotlinx.coroutines.flow.Flow // A Flow-hoz
import kotlinx.coroutines.flow.flow // A flow-hoz
import kotlinx.coroutines.flow.flowOn // A flowOn-hoz
import kotlinx.coroutines.runBlocking // A runBlocking-hoz

// Példa egy függvényre, ami egy Flow-t ad vissza
fun simpleFlow(): Flow<Int> = flow {
    logThread("Flow elindult") // A szál logolása, ahol a flow builder fut
    for (i in 1..3) {
        emit(i) // Értékek kibocsátása
    }
}

fun main() = runBlocking {
    simpleFlow()
        .flowOn(Dispatchers.IO) // Upstream (kibocsátók) Dispatchers.IO-ra vált
        .collect { value ->
            logThread("Gyűjtött $value") // A szál logolása, ahol az értékek gyűjtése történik
        }
}

// Segédfüggvény a szál logolásához
fun logThread(msg: String) {
    println("[$msg] [${Thread.currentThread().name}]")
}

A flowOn operátor befolyásolja a szálat, amin az operátorok előtte a láncban (beleértve a flow építőt) futnak. Az utána lévő operátorok a flowOn argumentumában megadott szálon futnak. Ha több flowOn van a láncban, mindegyik a saját előtti részt befolyásolja.

Példa több flowOn-nal:

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

fun simpleFlowWithMultipleFlowOn(): Flow<String> = flow {
    logThread("Flow építő kezdődött") // Az első `flowOn`-on fog futni
    emit("A")
    emit("B")
}.map {
    logThread("Mapping $it") // A második `flowOn`-on fog futni
    it.toLowerCase()
}.flowOn(Dispatchers.Default) // A második `flowOn` hatással van a map()-ra és a flow építőre
.filter {
    logThread("Szűrés $it") // A szálon fut, ahol a `collect` hívás történik (alapértelmezett main/runBlocking)
    true
}.flowOn(Dispatchers.IO) // Az első `flowOn` hatással van a map()-ra és a flow építőre

fun main() = runBlocking {
    simpleFlowWithMultipleFlowOn().collect { value ->
        logThread("Gyűjtött $value") // A runBlocking (main) szálon fut
    }
}

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

Ebben a példában:

  • A flow építő és a map a Dispatchers.Default-on futnak (mivel ez az utolsó flowOn, ami előttük van).
  • A filter a collect hívó szálán fut, azaz azon a szálon, ahol a runBlocking végrehajtódik (általában Main Androidon).

Fontos megérteni, hogy a flowOn közbenső puffert hoz létre, és nem "átlátszó" szálváltás.