Kotlin launchIn 收集流

238 阅读1分钟

一、核心机制与语法

  1. ‌**launchIn 的作用
    launchIn 是 Flow 的扩展函数,用于‌
    在指定协程作用域中启动 Flow 的收集**‌,返回一个 Job 对象用于协程管理。

    • 非阻塞特性:与 collect 不同,launchIn不会挂起当前协程‌,允许并发执行其他逻辑26。

    • 语法原型:

      
      fun <T> Flow<T>.launchIn(scope: CoroutineScope): Job
      
  2. collect 的对比

    特性collectlaunchIn
    执行方式阻塞当前协程直到流结束非阻塞,立即返回 Job
    适用场景需同步处理流数据需异步启动流收集并管理生命周期
    代码示例flow.collect { ... }flow.launchIn(scope)

二、基本使用与示例

  1. 简单示例

    fun main() = runBlocking { // 协程作用域
        val userFlow = flow {
            emit("用户1")
            delay(1000)
            emit("用户2")
        }
        
        // 使用 launchIn 启动流收集
        userFlow
            .onEach { println("接收到: $it") } // 中间操作符
            .launchIn(this) // 指定协程作用域
        
        delay(2000) // 等待流完成
    }
    

    输出:

    textCopy Code
    接收到: 用户1  
    接收到: 用户2
    
    • 关键点‌:通过 launchIn(this) 将流的收集绑定到当前协程作用域(runBlocking)26。
  2. 组合中间操作符

    flowOf(1, 2, 3)
        .map { it * 2 }
        .onEach { delay(500) }
        .launchIn(CoroutineScope(Dispatchers.IO)) // 指定 IO 线程池
    

三、生命周期管理

  1. 取消流收集

    • 通过 Job.cancel() 终止流收集:

      val job = userFlow.launchIn(scope)
      delay(1000)
      job.cancel() // 停止流收集
      
    • 自动取消‌:当协程作用域被取消(如界面销毁),流收集自动终止27。

  2. 结构化并发实践
    避免使用 GlobalScope,优先绑定到有明确生命周期的协程作用域(如 viewModelScope):

    class MyViewModel : ViewModel() {
        fun startFlow() {
            flow { /* ... */ }
                .launchIn(viewModelScope) // 随 ViewModel 销毁自动取消
        }
    }
    

四、错误处理

  1. 异常捕获

    • 使用 catch 操作符处理上游异常:

      flow { emit(1); throw RuntimeException() }
          .catch { e -> println("捕获异常: ${e.message}") }
          .launchIn(scope)
      
    • 结合 try-catch 包围 launchIn 启动逻辑:

      try {
          flow { /* ... */ }.launchIn(scope)
      } catch (e: Exception) {
          // 处理外部异常
      }
      

五、典型应用场景

  1. UI 层数据监听
    在 Android 中异步更新 UI,避免阻塞主线程:

    // ViewModel 中
    val dataFlow = repository.fetchData()
    
    fun observeData() {
        dataFlow
            .flowOn(Dispatchers.IO) // 切换至 IO 线程
            .onEach { updateUI(it) }
            .launchIn(viewModelScope) // 绑定到 ViewModel 生命周期
    }
    
  2. 多流并行收集
    同时启动多个流收集任务:

    coroutineScope {
        flowA.launchIn(this)
        flowB.launchIn(this)
    }
    

总结对比

场景‌**launchIn 适用性**‌关键操作
异步启动流收集✅ 非阻塞、并发执行flow.launchIn(scope)
需要精细控制生命周期✅ 结合协程作用域管理viewModelScope 或自定义作用域
异常处理需求✅ 结合 catchtry-catch.catch { ... }

通过 launchIn,开发者可以更灵活地管理 Flow 的收集生命周期,尤其适用于需要异步处理和结构化并发的场景