Sobes.tech
Middle

Ako možno prepínať vlákna v Flow? Na ktorom vlákne beží Flow?

sobes.tech AI

Odpoveď od AI

Podľa predvoleného nastavenia Flow beží na vlákne, z ktorého bol spustený. Prepnúť vlákna je možné pomocou operátora flowOn.

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

// Príklad funkcie, ktorá vracia Flow
fun simpleFlow(): Flow<Int> = flow {
    logThread("Flow začal") // Logovanie vlákna, kde sa spúšťa flow builder
    for (i in 1..3) {
        emit(i) // Emitovanie hodnôt
    }
}

fun main() = runBlocking {
    simpleFlow()
        .flowOn(Dispatchers.IO) // Prepneme upstream (emittery) na Dispatchers.IO
        .collect { value ->
            logThread("Zbierané $value") // Logovanie vlákna, kde sa zbierajú hodnoty
        }
}

// Pomocná funkcia na logovanie vlákna
fun logThread(msg: String) {
    println("[$msg] [${Thread.currentThread().name}]")
}

Operátor flowOn ovplyvňuje vlákno, na ktorom sú vykonávané operátory pred neho v reťazci (vrátane flow builderu). Operátory za flowOn sa vykonávajú na vlákne uvedenom v argumente flowOn. Ak je v reťazci viac flowOn, každý z nich ovplyvňuje časť reťazca pred ním.

Príklad s viacerými flowOn:

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

fun simpleFlowWithMultipleFlowOn(): Flow<String> = flow {
    logThread("Flow builder začal") // Bude bežať na prvom špecifikovanom flowOn
    emit("A")
    emit("B")
}.map {
    logThread("Mapovanie $it") // Bude bežať na druhom špecifikovanom flowOn
    it.toLowerCase()
}.flowOn(Dispatchers.Default) // Druhý flowOn ovplyvňuje map() a flow builder
.filter {
    logThread("Filtrovanie $it") // Bude bežať na vlákne, z ktorého sa volá collect (zvyčajne main/runBlocking)
    true
}.flowOn(Dispatchers.IO) // Prvý flowOn ovplyvňuje map() a flow builder

fun main() = runBlocking {
    simpleFlowWithMultipleFlowOn().collect { value ->
        logThread("Zbieranie $value") // Beží na vlákne runBlocking (main)
    }
}

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

V tomto príklade:

  • flow builder a map budú bežať na Dispatchers.Default (pretože je to posledný flowOn v reťazci pred nimi).
  • filter bude bežať na vlákne, z ktorého sa volá collect, teda na vlákne, kde sa vykonáva runBlocking (zvyčajne Main v Androidu).

Dôležité je pochopiť, že flowOn vytvára medzičasový buffer a nie je "priehľadným" prepínaním.