三種常見的 flow 建構器#

Kotlin 提供了多種方式來建立 Flow,本篇介紹最常用的三種 flow 建構器(Flow Builder):

  • flow { ... }
  • flowOf(...)
  • asFlow()

它們各自適合不同的場景,但內部其實都會走到 flow { ... }

flow#

最一般也最自由的建構方式,可以在 lambda 裡寫任意邏輯,再用 emit 送資料:

fun flow1(): Flow<Int> = flow {
    println("Flow started")
    repeat(10) {
        delay(100)
        emit(it)
    }
}

特色:

  • lambda 是 suspend 的,可以放 delay、其他 suspend 呼叫。
  • 適合來源是「需要計算或非同步取得」的資料。

flowOf#

flowOf 是專門針對「已知一組固定值」設計的 builder,包含兩個版本:

public fun <T> flowOf(vararg elements: T): Flow<T> = flow {
    for (element in elements) {
        emit(element)
    }
}
public fun <T> flowOf(value: T): Flow<T> = flow {
    /*
     * Implementation note: this is just an "optimized" overload of flowOf(vararg)
     * which significantly reduces the footprint of widespread single-value flows.
     */
    emit(value)
}

從原始碼可以看到,flowOf 內部其實也是用 flow { ... } 並逐一 emit。使用方式:

flowOf(1, 2, 3, 4)

特色:

  • 直觀、語意清楚,適合測試或固定常數來源。
  • 單值版本是針對「常見的單一值 flow」做了最佳化。

asFlow#

如果手上已經有一個 Collection、Range、Sequence 或函式,可以直接呼叫 asFlow() 把它轉成 Flow:

public fun <T> Array<T>.asFlow(): Flow<T> = flow {
    forEach { value ->
        emit(value)
    }
}

public fun IntArray.asFlow(): Flow<Int> = flow {
    forEach { value ->
        emit(value)
    }
}

public fun LongArray.asFlow(): Flow<Long> = flow {
    forEach { value ->
        emit(value)
    }
}

public fun IntRange.asFlow(): Flow<Int> = flow {
    forEach { value ->
        emit(value)
    }
}

public fun LongRange.asFlow(): Flow<Long> = flow {
    forEach { value ->
        emit(value)
    }
}
@FlowPreview
public fun <T> (() -> T).asFlow(): Flow<T> = flow {
    emit(invoke())
}

@FlowPreview
public fun <T> (suspend () -> T).asFlow(): Flow<T> = flow {
    emit(invoke())
}

public fun <T> Iterable<T>.asFlow(): Flow<T> = flow {
    forEach { value ->
        emit(value)
    }
}

public fun <T> Iterator<T>.asFlow(): Flow<T> = flow {
    forEach { value ->
        emit(value)
    }
}

public fun <T> Sequence<T>.asFlow(): Flow<T> = flow {
    forEach { value ->
        emit(value)
    }
}

特色:

  • 把現有資料結構轉成 Flow,便於套用中間運算子。
  • () -> Tsuspend () -> T 都有支援,可以把單一函式包成一個 Flow。

該選哪個 builder#

可以用幾個簡單的問題來判斷:

  • 資料是否要「動態產生」、需要 delay 或其他 suspend 行為?選 flow { ... }
  • 已經有「幾個固定值」要送出?選 flowOf(...)
  • 已經有 Collection、Range、Sequence、函式?選 asFlow()

小結#

  • 三個 builder 在底層都是包在 flow { ... } 裡用 emit
  • 差別主要在於語意:手寫邏輯、固定值清單、現成資料結構。
  • 三者建立出來的 Flow 都是冷資料流(Cold Stream),都要等 collect 才會跑。

原文出處#

  • 原書/iThome:https://ithelp.ithome.com.tw/articles/10269815
  • 原書/iThome:https://ithelp.ithome.com.tw/articles/10270555