Sobes.tech
Middle

Como é que se podem alternar os threads no Flow? Em qual thread o Flow funciona?

sobes.tech IA

Resposta da IA

Por padrão, o Flow funciona no thread a partir do qual foi iniciado. Pode alterar os threads usando o operador flowOn.

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

// Exemplo de função que retorna um Flow
fun simpleFlow(): Flow<Int> = flow {
    logThread("Flow iniciado") // Registo do thread onde o builder do flow é iniciado
    for (i in 1..3) {
        emit(i) // Emite valores
    }
}

fun main() = runBlocking {
    simpleFlow()
        .flowOn(Dispatchers.IO) // Altera upstream (emissores) para Dispatchers.IO
        .collect { value ->
            logThread("Coletado $value") // Registo do thread onde os valores são coletados
        }
}

// Função auxiliar para registar o thread
fun logThread(msg: String) {
    println("[$msg] [${Thread.currentThread().name}]")
}

O operador flowOn afeta o thread onde os operadores antes dele na cadeia (incluindo o flow builder) são executados. Os operadores depois de flowOn são executados no thread especificado no argumento flowOn. Se houver vários flowOn na cadeia, cada um afeta a parte da cadeia antes dele.

Exemplo com múltiplos flowOn:

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

fun simpleFlowWithMultipleFlowOn(): Flow<String> = flow {
    logThread("Builder de Flow iniciado") // Funciona no primeiro `flowOn` especificado
    emit("A")
    emit("B")
}.map {
    logThread("Mapeando $it") // Funciona no segundo `flowOn` especificado
    it.toLowerCase()
}.flowOn(Dispatchers.Default) // O segundo `flowOn` afeta o map() e o builder de flow
.filter {
    logThread("Filtrando $it") // Funciona no thread onde `collect` é chamado (por padrão main/runBlocking)
    true
}.flowOn(Dispatchers.IO) // O primeiro `flowOn` afeta o map() e o builder de flow

fun main() = runBlocking {
    simpleFlowWithMultipleFlowOn().collect { value ->
        logThread("Coletando $value") // Funciona no thread runBlocking (main)
    }
}

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

Neste exemplo:

  • O flow builder e map irão trabalhar em Dispatchers.Default (pois é o último flowOn na cadeia antes deles).
  • filter irá trabalhar no thread onde collect é chamado, ou seja, no thread onde runBlocking é executado (normalmente Main em Android).

É importante entender que flowOn cria um buffer intermediário e não é uma "mudança de thread transparente".