deepseek Kotlin Flow 全面详解

deepseek总结的文档,十分靠谱!微调了少量内容。

1. Flow 基础概念

1.1 什么是 Flow

Flow 是 Kotlin 协程库中的异步数据流处理工具,类似于 RxJava 的 Observable,但更简洁、与协程深度集成。

1.2 Flow 的特点

  • 冷流(Cold Stream):有收集者时才执行
  • 可取消的:基于协程的取消机制
  • 背压感知:自动处理生产消费速度不匹配
  • 丰富的操作符:类似集合操作但支持异步

2. Flow 的创建方式

2.1 基础创建方法

kotlin 复制代码
// 1. flow{} 构建器 - 最常用
fun simpleFlow(): Flow<Int> = flow {
    for (i in 1..3) {
        delay(100) // 可以调用挂起函数
        emit(i)    // 发射值
    }
}

// 2. asFlow() 扩展 - 从集合转换
fun collectionToFlow(): Flow<Int> = listOf(1, 2, 3).asFlow()

// 3. flowOf() - 从固定值创建
fun fixedFlow(): Flow<Int> = flowOf(1, 2, 3)

// 4. channelFlow - 更灵活的构建器
fun channelFlowExample(): Flow<Int> = channelFlow {
    send(1)
    withContext(Dispatchers.IO) {
        send(2) // 可以在不同上下文发送
    }
    send(3)
    close()
}

2.2 channelFlow{} vs flow{}

channelFlow和flow是Kotlin协程中处理数据流的两种方式,主要区别体现在以下几个方面:

  1. 数据流特性
    flow‌是冷数据流,只有在订阅者开始收集数据时才会触发数据生产,且每个订阅者会独立获得完整的数据序列。它类似于电梯,只有调用collect时才启动数据生产,结束后立即停止。
    channelFlow‌是热数据流的一种扩展,允许在数据生产时切换协程上下文,并内置缓冲区(默认64)。它更像地铁自动扶梯,数据生产独立于消费存在。
  2. 上下文限制
    flow‌严格要求在同一个协程上下文中发射数据,不允许通过withContext切换调度器。
    channelFlow‌通过send方法支持跨协程发送数据,内部使用Channel实现多协程并发生产。允许使用withContext。
  3. 缓冲机制
    flow‌需要显式调用.buffer()操作符添加缓冲功能。
    channelFlow‌天然具备缓冲区(也可通过.buffer()配置大小),且支持异步合并多个流(如通过launch并发收集)。
  4. 生命周期控制
    flow‌的生命周期与收集操作绑定,收集结束即终止。
    channelFlow‌可通过awaitClose保持通道开放,直到显式关闭。
    典型使用场景
    flow‌适用于单次数据提供场景(如数据库查询,API请求)。
    channelFlow‌适合事件合并、跨协程数据生产或需要背压控制的场景(如合并多个API响应)。

3. Flow 的终端操作符

3.1 收集操作符

kotlin 复制代码
suspend fun terminalOperators() {
    val numberFlow = (1..5).asFlow()
    
    // 1. collect - 基本收集
    numberFlow.collect { value -> println("收集: $value") }
    
    // 2. launchIn - 在指定作用域启动收集
    numberFlow
        .onEach { println("launchIn: $it") }
        .launchIn(CoroutineScope(Dispatchers.IO))
    
    // 3. toList/toSet - 转换为集合
    val list = numberFlow.toList()
    val set = numberFlow.toSet()
    println("转换为List: $list")
    
    // 4. first/firstOrNull - 获取第一个元素
    val first = numberFlow.first()
    val firstOrNull = emptyFlow<Int>().firstOrNull()
    println("第一个元素: $first")
    
    // 5. single/singleOrNull - 期望只有一个元素
    val single = flowOf(42).single()
    println("唯一元素: $single")
    
    // 6. count - 计数
    val count = numberFlow.count()
    println("元素数量: $count")
    
    // 7. reduce/fold - 聚合操作
    val sum = numberFlow.reduce { acc, value -> acc + value }
    val sumWithStart = numberFlow.fold(10) { acc, value -> acc + value }
    println("累加和: $sum, 带初始值的和: $sumWithStart")
}

4. Flow 的中间操作符

4.1 转换操作符

kotlin 复制代码
suspend fun transformOperators() {
    val flow = (1..3).asFlow()
    
    // 1. map - 转换每个元素
    flow.map { it * it }.collect { println("平方: $it") }
    
    // 2. transform - 更灵活的转换,可以发射多个值
    flow.transform { value ->
        if (value % 2 == 1) {
            emit(value)
            emit(value * 10)
        }
    }.collect { println("transform: $it") }
    
    // 3. mapLatest - 取消之前的转换,只处理最新的
    flow.mapLatest { value ->
        delay(100) // 模拟耗时操作
        "映射后的$value"
    }.collect { println("mapLatest: $it") }
}

4.2 过滤操作符

kotlin 复制代码
suspend fun filterOperators() {
    val flow = (1..10).asFlow()
    
    // 1. filter - 条件过滤
    flow.filter { it % 2 == 0 }.collect { println("偶数: $it") }
    
    // 2. filterNot - 反向过滤
    flow.filterNot { it > 5 }.collect { println("小于等于5: $it") }
    
    // 3. filterIsInstance - 类型过滤
    flowOf(1, "hello", 2, "world", 3)
        .filterIsInstance<String>()
        .collect { println("字符串: $it") }
    
    // 4. take - 取前n个
    flow.take(3).collect { println("前3个: $it") }
    
    // 5. drop - 丢弃前n个
    flow.drop(5).collect { println("丢弃前5个后: $it") }
    
    // 6. distinctUntilChanged - 去重连续重复
    flowOf(1, 1, 2, 2, 3, 3, 1)
        .distinctUntilChanged()
        .collect { println("去重连续重复: $it") }
}

4.3 组合操作符

kotlin 复制代码
suspend fun combinationOperators() {
    val flow1 = flowOf("A", "B", "C")
    val flow2 = flowOf(1, 2, 3)
    
    // 1. zip - 一对一组合
    flow1.zip(flow2) { a, b -> "$a$b" }
        .collect { println("zip: $it") } // A1, B2, C3
    
    // 2. combine - 最新值组合
    val numbers = flowOf(1, 2, 3).onEach { delay(100) }
    val letters = flowOf("A", "B", "C").onEach { delay(150) }
    
    numbers.combine(letters) { number, letter -> "$number$letter" }
        .collect { println("combine: $it") } // 1A, 2A, 2B, 3B, 3C
    
    // 3. merge - 合并多个流
    merge(flow1, flow2.map { it.toString() })
        .collect { println("merge: $it") } // A, B, C, 1, 2, 3
    
    // 4. flatMapConcat - 顺序展开
    flow1.flatMapConcat { value -> 
        flowOf("$value1", "$value2") 
    }.collect { println("flatMapConcat: $it") } // A1, A2, B1, B2, C1, C2
    
    // 5. flatMapMerge - 并发展开
    flow1.flatMapMerge { value ->
        flow {
            delay(100)
            emit("$value-快速")
            delay(200)
            emit("$value-慢速")
        }
    }.collect { println("flatMapMerge: $it") }
    
    // 6. flatMapLatest - 最新值展开
    flow1.flatMapLatest { value ->
        flow {
            emit("开始$value")
            delay(200) // 如果新值到来,会取消这个流
            emit("完成$value")
        }
    }.collect { println("flatMapLatest: $it") }
}

4.4 异常处理操作符

kotlin 复制代码
suspend fun exceptionHandling() {
    val flow = flow {
        emit(1)
        throw RuntimeException("模拟错误")
        emit(2) // 不会执行
    }
    
    // 1. catch - 捕获异常
    flow.catch { e -> 
        println("捕获异常: ${e.message}")
        emit(-1) // 可以恢复发射
    }.collect { println("catch: $it") }
    
    // 2. retry - 重试
    var attempt = 0
    flow.retry(2) { cause ->
        attempt++
        println("第${attempt}次重试,原因: ${cause.message}")
        attempt < 2 // 条件重试
    }.catch { e -> 
        emit(999) 
    }.collect { println("retry: $it") }
    
    // 3. retryWhen - 更灵活的重试
    flow.retryWhen { cause, attempt ->
        if (attempt < 3 && cause is RuntimeException) {
            delay(1000 * attempt)
            true
        } else {
            false
        }
    }.catch { e -> 
        println("最终失败: ${e.message}")
    }.collect { println("retryWhen: $it") }
}

4.5 回调操作符

kotlin 复制代码
suspend fun callbackOperators() {
    val flow = (1..3).asFlow()
    
    // 1. onEach - 每个元素处理前的回调
    flow.onEach { println("即将发射: $it") }
        .collect { println("收集到: $it") }
    
    // 2. onStart - 流开始前的回调
    flow.onStart { 
        println("流开始执行")
        emit(0) // 可以提前发射值
    }.collect { println("onStart: $it") }
    
    // 3. onCompletion - 流完成后的回调
    flow.onCompletion { cause ->
        if (cause == null) {
            println("流正常完成")
        } else {
            println("流异常完成: ${cause.message}")
        }
    }.collect { println("onCompletion: $it") }
    
    // 4. onEmpty - 流为空时的回调
    emptyFlow<Int>().onEmpty { 
        println("流为空,发射默认值")
        emit(-1)
    }.collect { println("onEmpty: $it") }
}

5. 上下文和调度

5.1 flowOn 操作符

kotlin 复制代码
suspend fun flowOnExample() {
    // flowOn 影响上游操作的上下文
    flow {
        // 这个块在 IO 线程执行
        println("发射线程: ${Thread.currentThread().name}")
        emit(1)
        emit(2)
    }
    .flowOn(Dispatchers.IO) // 指定上游上下文
    .map { 
        // 这个map在默认上下文执行(因为后面没有flowOn)
        println("映射线程: ${Thread.currentThread().name}")
        it * 2 
    }
    .flowOn(Dispatchers.Default) // 可以再次改变上游上下文
    .collect { 
        // 收集在调用协程的上下文
        println("收集线程: ${Thread.currentThread().name}, 值: $it")
    }
}

5.2 buffer 操作符 - 背压处理

kotlin 复制代码
suspend fun bufferOperators() {
    val slowFlow = flow {
        for (i in 1..3) {
            delay(100) // 模拟慢生产者
            emit(i)
            println("发射: $i")
        }
    }
    
    println("=== 无缓冲 ===")
    val time1 = measureTimeMillis {
        slowFlow.collect { value ->
            delay(300) // 模拟慢消费者
            println("处理: $value")
        }
    }
    println("总时间: ${time1}ms")
    
    println("=== 有缓冲 ===")
    val time2 = measureTimeMillis {
        slowFlow
            .buffer() // 添加缓冲,生产消费可以并发
            .collect { value ->
                delay(300)
                println("处理: $value")
            }
    }
    println("总时间: ${time2}ms")
    
    // 其他缓冲策略
    slowFlow.conflate() // 合并,只处理最新值
        .collect { println("conflate: $it") }
    
    slowFlow.collectLatest { value -> // 取消慢的处理,处理最新值
        println("开始处理: $value")
        delay(400)
        println("完成处理: $value") // 可能被取消
    }
}

6. 共享流(SharedFlow 和 StateFlow)

6.1 shareIn - 冷流转热流

api为:

kotlin 复制代码
fun <T> Flow<T>.shareIn(
    scope: CoroutineScope,
    started: SharingStarted,
    replay: Int = 0
): SharedFlow<T>
  • 不需要初始值
  • 返回 SharedFlow,不能直接访问当前值
  • 可以配置重放数量(replay)
  • 适合事件、通知、一次性操作
示例1:
kotlin 复制代码
val notifications: SharedFlow<Notification> = notificationFlow
    .shareIn(
        scope = viewModelScope,
        started = SharingStarted.Eagerly,
        replay = 1  // 新订阅者收到最近1个通知
    )

// 不能直接访问当前值,只能通过收集
notifications.collect { notification ->
    showToast(notification.message)
}
示例2:
kotlin 复制代码
class DataRepository {
    // 冷流:每次收集都会重新执行
    private val dataFlow = flow {
        println("执行数据获取")
        emit(fetchDataFromNetwork())
    }
    
    // 使用 shareIn 转换为热流
    private val sharedFlow = dataFlow.shareIn(
        scope = CoroutineScope(Dispatchers.IO),
        started = SharingStarted.WhileSubscribed(
            stopTimeoutMillis = 5000, // 最后一个订阅者取消后5秒停止
            replayExpirationMillis = 0 // 立即过期重放数据
        ),
        replay = 1 // 新订阅者重放1个最新值
    )
    
    fun getData(): Flow<String> = sharedFlow
    
    private suspend fun fetchDataFromNetwork(): String {
        delay(1000)
        return "网络数据 ${System.currentTimeMillis()}"
    }
}

suspend fun shareInExample() {
    val repository = DataRepository()
    
    // 第一个订阅者
    val job1 = launch {
        repository.getData().collect { println("订阅者1: $it") }
    }
    
    delay(500)
    
    // 第二个订阅者 - 共享同一个数据流
    val job2 = launch {
        repository.getData().collect { println("订阅者2: $it") }
    }
    
    delay(2000)
    job1.cancel()
    job2.cancel()
}

6.2 stateIn - 转换为状态流

api为:

kotlin 复制代码
fun <T> Flow<T>.stateIn(
    scope: CoroutineScope,
    started: SharingStarted,
    initialValue: T
): StateFlow<T>
  • 必须有初始值
  • 返回 StateFlow,可以通过 .value 访问当前状态
  • 自动去重连续相同的值
  • 适合表示UI状态、应用状态
kotlin 复制代码
class CounterViewModel : ViewModel() {
    private val tickFlow = flow {
        var count = 0
        while (true) {
            delay(1000)
            emit(count++)
        }
    }
    
    // 使用 stateIn 转换为状态流
    val counterState = tickFlow.stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        initialValue = 0
    )
}

suspend fun stateInExample() {
    val viewModel = CounterViewModel()
    
    // 状态流总是有当前值
    viewModel.counterState.collect { value ->
        println("当前计数: $value")
    }
}

对于普通flow{} 我们通过上述代码来转换。而对于多个Stateflow组合,combine之后会退化为普通flow,需要重新stateIn变成StateFlow:

kotlin 复制代码
val _uiState = MutableStateFlow<UiState>(UiState.Initial)
val bState = MutableStateFlow<BState>(BState.Inital)

val uiState: StateFlow<UiState> = _uiState.combine(bState) { ui, bState->
	xxxx
}.stateIn(scope, SharingStarted.WhileSubscribed(5000), _uiState.value)

6.3 launchIn - 在指定作用域启动

kotlin 复制代码
suspend fun launchInExample() {
    val eventFlow = flow {
        repeat(5) {
            delay(500)
            emit("事件$it")
        }
    }
    
    val scope = CoroutineScope(Dispatchers.IO + CoroutineName("MyScope"))
    
    // 使用 launchIn 在指定作用域启动收集
    val job = eventFlow
        .onEach { event -> 
            println("处理事件: $event 在线程: ${Thread.currentThread().name}")
        }
        .launchIn(scope) // 返回Job可以取消
    
    delay(2000)
    println("取消收集")
    job.cancel()
}

6.4 StateFlow vs flow{}

StateFlow 特性:

  • 热流(Hot Flow):不管有没有收集者,都会存在并发射数据
  • 有状态 :始终持有当前值,可以通过 .value 访问
  • 可变的:可以直接更新值
  • 重复值抑制:默认不会发射相同的连续值
  • 多播:多个收集者共享同一个状态

flow{}特性:

  • 冷流(Cold Flow):只有在被收集时才会执行
  • 无状态:不持有当前值
  • 不可变的 :只能通过 emit 发射值
  • 每次收集重新执行:每个收集者都会触发流的重新执行
  • 单播:每个收集者获得独立的流执行
特性 StateFlow flow{}
类型 热流 冷流
状态 有状态,持有当前值 无状态
可变性 可变,可直接更新 不可变
执行时机 立即存在 收集时执行
重复收集 共享相同执行 每次重新执行
使用场景 状态管理、UI状态 异步操作、数据转换

使用场景举例:

kotlin 复制代码
// MutableStateFlow: UI 状态管理
class ViewModel {
    private val _uiState = MutableStateFlow<UiState>(Loading)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    
    fun loadData() {
        _uiState.value = Success(data)
    }
}

// flow{} 网络请求、数据库查询等一次性操作
fun fetchUserData(userId: String): Flow<User> = flow {
    val user = apiService.getUser(userId)
    emit(user)
}

// flow{} 复杂的数据转换
fun processData(input: Flow<Int>): Flow<String> = flow {
    input.collect { number ->
        val processed = heavyProcessing(number)
        emit(processed.toString())
    }
}

6.5 shareIn vs stateIn

stateInshareIn 都是用于将冷流转换为热流的操作符,但它们在行为和用途上有重要区别。

基本区别

特性 stateIn shareIn
返回类型 StateFlow SharedFlow
初始值 必须提供 可选(通过 replay)
状态特性 始终有当前值 可能没有值(如果没有发射过)
重复值抑制 自动抑制连续相同值 默认不抑制,可配置
使用场景 状态管理 事件广播、数据共享
SharingStarted 策略

两者都使用相同的启动策略:

1. SharingStarted.Eagerly
kotlin 复制代码
// 立即启动,不管有没有订阅者
started = SharingStarted.Eagerly
2. SharingStarted.Lazily
kotlin 复制代码
// 在第一个订阅者出现时启动
started = SharingStarted.Lazily
3. SharingStarted.WhileSubscribed
kotlin 复制代码
// 有订阅者时启动,无订阅者时停止
// 可以配置超时和重放过期
started = SharingStarted.WhileSubscribed(
    stopTimeoutMillis = 0,
    replayExpirationMillis = 0
)

// 保留5秒,防止快速重新订阅
started = SharingStarted.WhileSubscribed(5000)
实际使用场景对比
stateIn 适合的场景:
kotlin 复制代码
class UserViewModel : ViewModel() {
    // UI状态 - 使用 stateIn
    val uiState: StateFlow<UiState> = userRepository
        .getUserStream()
        .map { user -> 
            UiState.Success(user) 
        }
        .catch { e -> 
            emit(UiState.Error(e.message ?: "Unknown error"))
        }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = UiState.Loading
        )
}
shareIn 适合的场景:
kotlin 复制代码
class EventBus {
    // 事件总线 - 使用 shareIn
    private val _events = MutableSharedFlow<Event>()
    
    val events: SharedFlow<Event> = _events
        .shareIn(
            scope = CoroutineScope(Dispatchers.Default),
            started = SharingStarted.Eagerly,
            replay = 0
        )
    
    fun sendEvent(event: Event) {
        _events.tryEmit(event)
    }
}

// 一次性操作结果
val searchResults: SharedFlow<SearchResult> = searchFlow
    .shareIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(),
        replay = 1
    )
选择指南
使用 stateIn 当:
  • 你需要表示状态(有当前值)
  • 你需要直接访问当前值(通过 .value
  • 你希望自动去重连续相同的值
  • 用于UI状态、数据状态管理
使用 shareIn 当:
  • 你需要广播事件或通知
  • 不需要直接访问当前值
  • 需要控制重放数量
  • 用于一次性操作、事件总线、消息传递
小结:
  • stateInStateFlow → 用于状态,有当前值,自动去重
  • shareInSharedFlow → 用于事件,无当前值,可配置重放

根据你的具体需求选择合适的操作符,大多数UI状态管理场景使用 stateIn,而事件处理使用 shareIn

7. 高级特性

7.1 自定义操作符

kotlin 复制代码
// 自定义过滤操作符
fun <T> Flow<T>.filterWithLog(predicate: (T) -> Boolean): Flow<T> = flow {
    collect { value ->
        if (predicate(value)) {
            println("过滤通过: $value")
            emit(value)
        } else {
            println("过滤拒绝: $value")
        }
    }
}

// 自定义转换操作符
fun Flow<Int>.runningSum(): Flow<Int> = flow {
    var sum = 0
    collect { value ->
        sum += value
        emit(sum)
    }
}

suspend fun customOperators() {
    (1..5).asFlow()
        .filterWithLog { it % 2 == 0 }
        .runningSum()
        .collect { println("运行和: $it") }
}

7.2 复杂组合示例

kotlin 复制代码
suspend fun complexFlowExample() {
    // 模拟用户输入流
    val userInputs = flowOf("A", "B", "C", "D", "E")
        .onEach { delay(200) } // 模拟用户输入间隔
    
    // 模拟网络请求流
    fun simulateNetworkRequest(input: String): Flow<String> = flow {
        delay(500) // 模拟网络延迟
        emit("响应: $input")
    }
    
    userInputs
        .onStart { println("开始监听用户输入") }
        .onEach { println("用户输入: $it") }
        .flatMapMerge(concurrency = 3) { input -> // 并发处理最多3个请求
            simulateNetworkRequest(input)
                .onStart { println("开始请求: $input") }
                .catch { e -> emit("错误: ${e.message}") }
        }
        .onEach { println("收到响应: $it") }
        .buffer() // 防止背压
        .catch { e -> println("流异常: ${e.message}") }
        .onCompletion { println("流完成") }
        .collect()
}

8. 实际应用案例

8.1 搜索建议功能

kotlin 复制代码
class SearchViewModel : ViewModel() {
    private val searchQuery = MutableStateFlow("")
    
    val searchResults = searchQuery
        .debounce(300) // 防抖300ms
        .filter { it.length >= 3 } // 至少3个字符
        .distinctUntilChanged() // 查询相同时不重复搜索
        .flatMapLatest { query -> // 取消之前的搜索
            performSearch(query)
                .catch { e -> emit(emptyList()) }
        }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )
    
    fun onSearchQueryChanged(query: String) {
        searchQuery.value = query
    }
    
    private fun performSearch(query: String): Flow<List<String>> = flow {
        // 模拟网络搜索
        delay(1000)
        val results = listOf("${query}结果1", "${query}结果2", "${query}结果3")
        emit(results)
    }
}

8.2 实时数据更新

kotlin 复制代码
class StockMonitor {
    private val stockPrices = MutableSharedFlow<StockPrice>(
        replay = 10, // 重放最近10个价格
        extraBufferCapacity = 50
    )
    
    // 模拟实时价格更新
    fun startMonitoring() {
        CoroutineScope(Dispatchers.IO).launch {
            var price = 100.0
            while (true) {
                delay(1000)
                price += (Math.random() - 0.5) * 10 // 随机波动
                stockPrices.emit(StockPrice("AAPL", price, System.currentTimeMillis()))
            }
        }
    }
    
    fun getPriceUpdates(): Flow<StockPrice> = stockPrices
    
    // 价格变化率计算
    fun getPriceChanges(): Flow<Double> = stockPrices
        .take(2) // 取最近两个价格
        .map { it.price }
        .runningReduce { old, new -> ((new - old) / old) * 100 } // 计算变化率
}

data class StockPrice(val symbol: String, val price: Double, val timestamp: Long)

9. 性能优化和最佳实践

9.1 性能优化技巧

kotlin 复制代码
suspend fun performanceTips() {
    val flow = (1..1000).asFlow()
    
    // 1. 合理使用 buffer
    flow
        .map { heavyOperation(it) }
        .buffer() // 允许map并发执行
        .collect { println(it) }
    
    // 2. 避免不必要的操作链
    flow
        .filter { it % 2 == 0 }
        .map { it * 2 } // 先过滤再映射,减少操作次数
        .collect()
    
    // 3. 使用 flowOn 合理分配上下文
    flow
        .map { cpuIntensiveOperation(it) }
        .flowOn(Dispatchers.Default) // CPU密集型在Default调度器
        .map { ioOperation(it) }
        .flowOn(Dispatchers.IO) // IO操作在IO调度器
        .collect()
}

suspend fun heavyOperation(value: Int): Int {
    delay(10)
    return value * 2
}

suspend fun cpuIntensiveOperation(value: Int): Int {
    // 模拟CPU密集型操作
    return value * value
}

suspend fun ioOperation(value: Int): String {
    delay(50) // 模拟IO操作
    return "结果: $value"
}

9.2 测试 Flow

kotlin 复制代码
@Test
fun `test flow operations`() = runTest { // 使用TestScope
    val flow = flowOf(1, 2, 3, 4, 5)
    
    val result = flow
        .filter { it % 2 == 0 }
        .map { it * 2 }
        .toList()
    
    assertEquals(listOf(4, 8), result)
}

// 测试超时和异常
@Test
fun `test flow timeout`() = runTest {
    val slowFlow = flow {
        emit(1)
        delay(1000) // 长时间延迟
        emit(2)
    }
    
    assertFailsWith<TimeoutCancellationException> {
        withTimeout(500) { // 设置超时
            slowFlow.collect()
        }
    }
}

10. Flow 与 Channel 的比较

特性 Flow Channel
数据模式 数据流(可能无限) 点对点通信
背压处理 自动处理 需要手动配置缓冲
冷热性 冷流(默认) 热流
使用场景 数据处理管道 协程间通信
多个消费者 每个消费者独立流 共享数据

通过全面掌握 Flow 的各种操作符和特性,你可以构建出高效、响应式的异步数据处理管道,充分发挥 Kotlin 协程在异步编程中的优势。

相关推荐
ii_best1 小时前
IOS/ 安卓开发工具按键精灵Sys.GetAppList 函数使用指南:轻松获取设备已安装 APP 列表
android·开发语言·ios·编辑器
2501_915909061 小时前
iOS 26 文件管理实战,多工具组合下的 App 数据访问与系统日志调试方案
android·ios·小程序·https·uni-app·iphone·webview
limingade3 小时前
手机转SIP-手机做中继网关-落地线路对接软交换呼叫中心
android·智能手机·手机转sip·手机做sip中继网关·sip中继
RainbowC03 小时前
GapBuffer高效标记管理算法
android·算法
程序员码歌3 小时前
豆包Seedream4.0深度体验:p图美化与文生图创作
android·前端·后端
、花无将4 小时前
PHP:下载、安装、配置,与apache搭建
android·php·apache
shaominjin1235 小时前
Android 约束布局(ConstraintLayout)的权重机制:用法与对比解析
android·网络
我命由我123456 小时前
Android 对话框 - 对话框全屏显示(设置 Window 属性、使用自定义样式、继承 DialogFragment 实现、继承 Dialog 实现)
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
怪兽20147 小时前
请例举 Android 中常用布局类型,并简述其用法以及排版效率
android·面试
应用市场7 小时前
Android Bootloader启动逻辑深度解析
android