Kotlin Flow

6 阅读12分钟

Flow 是 Kotlin 协程库中的响应式流处理组件,它 ->允许数据以异步流的形式进行发射和收集**,特别适合处理实时数据流和异步操作

一、Flow 基础概念

1. Flow 是什么?

  • 冷流(Cold Stream) :只有被收集时才会执行生产代码
  • 可取消的:基于协程的取消机制
  • 背压(Backpressure)支持:自动处理生产者和消费者速度不匹配问题

2. 基本结构

flow {
    // 生产者代码块
    emit(1)  // 发射值
    emit(2)
}.collect { value ->
    // 消费者代码块
    println(value)
}

二、Flow 创建方式

1. 基础创建方法

// 1. flow {} 构建器
val flow1 = flow {
    (1..5).forEach {
        delay(100)
        emit(it)
    }
}

// 2. asFlow() 扩展
val flow2 = (1..5).asFlow()

// 3. flowOf() 快捷函数
val flow3 = flowOf("A", "B", "C")

// 4. 从回调API转换
fun callbackFlowExample(): Flow<Int> = callbackFlow {
    val callback = object : SomeCallback {
        override fun onNextValue(value: Int) {
            trySend(value)
        }
        override fun onComplete() {
            close()
        }
    }
    registerCallback(callback)
    awaitClose { unregisterCallback(callback) }
}

2. 特殊 Flow 创建

// 空流
val emptyFlow = emptyFlow<Int>()

// 错误流
val errorFlow = flow<Int> { 
    throw RuntimeException("Error") 
}

// 单值流
val singleValueFlow = flowOf(42)

三、Flow 操作符大全

1. 转换操作符: map(),transform(),withIndex(),scan()

flow.map { it * 2 }  // 值转换

flow.transform { value ->
    emit("Value $value")
    emit("Length ${value.toString().length}")
}

flow.withIndex()  // 添加索引

flow.scan(initial = 0) { acc, value -> acc + value }  // 累积计算

2. 过滤操作符

flow.filter { it % 2 == 0 }  // 过滤偶数

flow.filterNot { it.isBlank() }  // 过滤空白

flow.take(3)  // 取前3个

flow.drop(2)  // 跳过前2个

flow.distinctUntilChanged()  // 去重连续相同值

3. 组合操作符

flow1.zip(flow2) { a, b -> a + b }  // 一对一合并

flow1.combine(flow2) { a, b -> a * b }  // 最新值组合

flow1.merge(flow2)  // 简单合并

flow1.flattenConcat()  // 顺序展平

flow1.flattenMerge()  // 并发展平

4. 异常处理

flow.catch { e -> 
    emit(-1)  // 捕获上游异常
    // 可以重新抛出或发射替代值
}.onCompletion { cause -> 
    // 完成时回调(无论正常或异常)
}

5. 线程切换

flow.flowOn(Dispatchers.IO)  // 指定上游执行上下文

flow.buffer()  // 添加缓冲区

flow.conflate()  // 合并快速发射的值

flow.collectLatest {  // 取消前一个收集处理新值
    // 只处理最新值
}

四、Flow 终端操作

1. 基本收集

flow.collect { value -> 
    println(value) 
}

// 简化收集
flow.onEach { println(it) }.launchIn(viewModelScope)

2. 聚合操作

val sum = flow.reduce { acc, value -> acc + value }

val first = flow.first()

val last = flow.last()

val count = flow.count()

3. 转换为集合

val list = flow.toList()

val set = flow.toSet()

五、Flow 高级用法

1. StateFlow (状态流)

// 创建
private val _state = MutableStateFlow(0)
val state: StateFlow<Int> = _state.asStateFlow()

// 更新
_state.value = 42

// 收集
state.collect { value -> 
    // 会自动去重,只有值变化时触发
}

2. SharedFlow (共享流)

// 创建
private val _events = MutableSharedFlow<Event>()
val events: SharedFlow<Event> = _events.asSharedFlow()

// 发射事件
_events.emit(Event.SomethingHappened)

// 收集
events.collect { event ->
    // 处理事件
}

3. Flow 与 LiveData 互转

// Flow → LiveData
flow.asLiveData()

// LiveData → Flow
liveData.asFlow()

六、实际应用场景

1. 网络请求 + 数据库

fun getProducts(): Flow<List<Product>> = flow {
    // 先显示缓存
    emit(dao.getCachedProducts())
    
    // 发起网络请求
    val freshProducts = api.fetchProducts()
    
    // 更新缓存
    dao.insertAll(freshProducts)
    
    // 发射新数据
    emit(freshProducts)
}.catch { e ->
    // 错误处理
    emit(dao.getCachedProducts())
}

2. 搜索建议

searchQuery
    .debounce(300)  // 防抖
    .filter { it.length > 2 }
    .distinctUntilChanged()
    .flatMapLatest { query ->
        api.searchSuggestions(query)
            .catch { emit(emptyList()) }
    }
    .flowOn(Dispatchers.IO)
    .collect { suggestions ->
        updateUI(suggestions)
    }

3. 多数据源合并

val userFlow = userRepository.getUser()
val postsFlow = postsRepository.getPosts()

userFlow.combine(postsFlow) { user, posts ->
    UserWithPosts(user, posts)
}.collect { userWithPosts ->
    showProfile(userWithPosts)
}

七、性能优化技巧

  1. 适当使用 buffer() :当生产者和消费者速度不匹配时

    flow.buffer(32)  // 设置合理缓冲区大小
    
  2. 使用 shareIn 共享冷流

    val sharedFlow = originalFlow.shareIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        replay = 1
    )
    
  3. 避免不必要的 flowOn:频繁切换调度器有开销

  4. 适时使用 conflate() :当只需要最新值时

八、常见问题解决

  1. 流不触发收集

    • 检查协程作用域是否存活
    • 确认流是否被正确订阅
  2. 背压问题

    flow.buffer()  // 添加缓冲区
    flow.conflate()  // 合并快速发射的值
    
  3. 内存泄漏

    • 使用 lifecycleScope/viewModelScope
    • 适时取消协程
  4. 重复订阅

    • 使用 StateFlow/SharedFlow 替代普通 Flow
    • 使用 shareIn 共享冷流

Kotlin Flow 解决的问题及其核心价值

Kotlin Flow 是 Kotlin 协程库中的响应式流处理组件,它主要解决了以下关键问题:

一、异步数据流处理的复杂性

1. 简化多值异步操作

传统回调或 Future/Promise 模式在处理多个异步产生的值时非常笨拙:

// 旧方式:回调地狱示例
fun fetchData(callback: (List<Data>) -> Unit) {
    api.getData { data ->
        api.getMoreData(data.id) { moreData ->
            callback(moreData.items)
        }
    }
}

// Flow 解决方案
fun fetchData(): Flow<List<Data>> = flow {
    val data = api.getData()
    val moreData = api.getMoreData(data.id)
    emit(moreData.items)
}

2. 解决背压(Backpressure)问题

Flow 内置背压处理机制,自动处理生产者与消费者速度不匹配的情况:

flow {
    // 快速发射值
    (1..1000).forEach { emit(it) }
}.collect { value ->
    // 慢速处理
    delay(100)
    println(value)
}
// 自动调节发射速率,不会内存溢出

二、替代传统响应式方案的痛点

1. 相比 RxJava 的优势

问题RxJava 方案Kotlin Flow 方案
学习曲线操作符繁多(300+)精简的操作符集(~50)
协程集成需要额外适配原生协程支持
取消机制需要手动管理自动协程取消
线程切换复杂的调度器简单 flowOn 操作

2. 实际代码对比

// RxJava
Observable.interval(1, TimeUnit.SECONDS)
    .observeOn(Schedulers.io())
    .subscribeOn(AndroidSchedulers.mainThread())
    .subscribe { value ->
        // 处理值
    }

// Kotlin Flow
flow {
    var counter = 0
    while(true) {
        emit(counter++)
        delay(1000)
    }
}.flowOn(Dispatchers.IO)
.collect { value ->
    // 自动在主线程收集
}

三、统一异步编程模型

1. 解决异步操作类型碎片化

在 Flow 之前,Android 开发需要处理多种异步模式:

  • 单次异步:CallbackFuture
  • 多值异步:RxJavaLiveData
  • 数据库变化:Room 的 Flowable

Flow 统一了所有这些场景

// 网络请求
fun fetchUser(): Flow<User>

// 数据库观察
fun getUsers(): Flow<List<User>>

// UI事件
val clicks: Flow<Unit>

2. 与协程深度集成

// 轻松与其他协程代码结合
viewModelScope.launch {
    val user = async { userRepo.fetchUser() }
    val posts = async { postsRepo.fetchPosts() }
    
    userFlow.combine(postsFlow) { u, p -> 
        UserWithPosts(u.await(), p.await())
    }.collect { combo ->
        updateUI(combo)
    }
}

四、生命周期感知与资源管理

1. 自动取消机制

Flow 收集会自动绑定到 协程作用域的生命周期

viewModelScope.launch {
    // 当 viewModelScope 取消时自动停止收集
    userFlow.collect { user ->
        updateUI(user)
    }
}

2. 安全的内存管理

相比 LiveData 需要手动观察生命周期:

// LiveData 方式
liveData.observe(viewLifecycleOwner) { value -> ... }

// Flow 自动处理
lifecycleScope.launch {
    flow.collect { value -> ... }
}

五、具体解决的问题场景

1. 实时数据管道

// 搜索建议实时处理
searchQuery
    .debounce(300)
    .filter { it.length > 2 }
    .flatMapLatest { query ->
        api.search(query).catch { emit(emptyList()) }
    }
    .collect { results ->
        showSuggestions(results)
    }

2. 多数据源组合

// 合并用户数据和设置
val userFlow = userRepository.getUser()
val settingsFlow = settingsRepository.getSettings()

userFlow.combine(settingsFlow) { user, settings ->
    UserProfile(user, settings)
}.collect { profile ->
    showProfile(profile)
}

3. 事件总线模式

// 使用 SharedFlow 替代 EventBus
private val _events = MutableSharedFlow<Event>()
val events: SharedFlow<Event> = _events.asSharedFlow()

// 发送事件
_events.emit(Event.Refresh)

// 接收事件
events.collect { event ->
    when(event) {
        Event.Refresh -> reloadData()
    }
}

六、架构层面的改进

1. 在 MVVM 中的典型应用

class MyViewModel : ViewModel() {
    private val _state = MutableStateFlow(State.LOADING)
    val state: StateFlow<State> = _state.asStateFlow()

    fun loadData() {
        viewModelScope.launch {
            repository.fetchData()
                .onStart { _state.value = State.LOADING }
                .catch { _state.value = State.ERROR(it) }
                .collect { data ->
                    _state.value = State.SUCCESS(data)
                }
        }
    }
}

2. 解决 LiveData 的局限性

LiveData 问题Flow 解决方案
只能在主线程观察支持多线程切换
有限的转换操作丰富的操作符
不擅长错误处理完善的 catch 机制
单Activity作用域任意协程作用域

总结:Flow 的核心价值

  1. 统一:提供处理所有异步数据流的统一API
  2. 简洁:基于协程的声明式代码比回调更易读
  3. 安全:自动取消和资源清理
  4. 灵活:丰富的操作符满足各种数据处理需求
  5. 高效:轻量级设计,性能接近原生代码
  • Flow 特别适合现代 Android 开发中的复杂异步场景,是 Jetpack 组件和协程生态的重要桥梁,有效解决了传统异步编程中的诸多痛点。

SharedFlow 为什么适合高频数据

SharedFlow 作为 Kotlin 协程中的热流(hot stream)实现,特别适合处理高频数据场景(如 WebSocket、传感器数据、实时消息等),这主要得益于其以下几个关键设计特性:

一、内存缓冲机制

1. 可配置的缓冲区

val sharedFlow = MutableSharedFlow<Int>(
    replay = 2,          // 新订阅者收到的最近2个值
    extraBufferCapacity = 50, // 额外缓冲区容量
    onBufferOverflow = BufferOverflow.SUSPEND // 缓冲区满时的策略
)

优势

  • 当生产者速度 > 消费者速度时,数据不会丢失
  • 可平衡生产消费速率差异
  • 避免背压(backpressure)问题导致崩溃

2. 灵活的溢出策略

enum class BufferOverflow {
    SUSPEND,    // 默认,挂起发射端
    DROP_OLDEST, // 丢弃最旧的值
    DROP_LATEST  // 丢弃最新的值
}

二、多订阅者支持

1. 真正的多播(Multicast)

val sharedFlow = MutableSharedFlow<Int>()

// 生产者
launch {
    (1..100).forEach {
        sharedFlow.emit(it)
        delay(10)
    }
}

// 消费者1
launch {
    sharedFlow.collect { value ->
        println("Consumer1: $value")
    }
}

// 消费者2 (可随时加入)
launch {
    delay(100)
    sharedFlow.collect { value ->
        println("Consumer2: $value")
    }
}

优势

  • 新订阅者可以获取历史数据(replay)
  • 每个订阅者独立处理数据流
  • 避免为每个订阅者创建新流

三、高性能的并发设计

1. 无锁并发实现

SharedFlow 内部采用高效的并发数据结构:

  • 基于数组的环形缓冲区
  • 原子操作保证线程安全
  • 最小化同步开销

2. 协程友好的取消

val job = launch {
    sharedFlow.collect { value ->
        if (value == 42) {
            cancel() // 安全取消收集
        }
    }
}

// 取消后不影响其他收集者

四、与 LiveData 的对比

特性SharedFlowLiveData
数据保留策略可配置的缓冲区和replay仅保留最新值
线程控制通过flowOn指定强制主线程观察
多订阅者支持真正多播单播(最后一个观察者生效)
背压处理内置缓冲和溢出策略无处理,直接覆盖
生命周期感知需配合Lifecycle.repeatOnLifecycle原生支持
适合场景高频数据流低频UI状态更新

五、实际高频场景示例

1. WebSocket 消息处理

private val _socketEvents = MutableSharedFlow<SocketEvent>(
    replay = 0,
    extraBufferCapacity = 100,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
)

// 接收消息
socket.onMessage { message ->
    _socketEvents.tryEmit(SocketEvent.Message(message))
}

// 收集处理
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        _socketEvents.collect { event ->
            when(event) {
                is SocketEvent.Message -> updateUI(event.content)
                // 其他事件类型...
            }
        }
    }
}

2. 传感器数据流

val sensorFlow = callbackFlow {
    val sensorListener = object : SensorEventListener {
        override fun onSensorChanged(event: SensorEvent) {
            trySend(event.values) // 快速发射数据
        }
        // ...
    }
    sensorManager.registerListener(sensorListener, ...)
    awaitClose { sensorManager.unregisterListener(sensorListener) }
}.shareIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(5000),
    replay = 1
)

// 多个消费者可以同时处理数据

六、性能优化技巧

  1. 合理配置缓冲区大小

    // 根据业务特点设置
    MutableSharedFlow<Data>(
        replay = 3, // 新订阅者获取最近3个值
        extraBufferCapacity = when {
            isLowEndDevice -> 20
            else -> 100
        }
    )
    
  2. 选择合适的溢出策略

    • DROP_OLDEST:适用于实时性要求高的场景(如游戏帧数据)
    • SUSPEND:适用于不能丢失数据的场景(如金融交易)
  3. 使用shareIn共享冷流

    val sharedData = sourceFlow
        .map { /* 转换 */ }
        .shareIn(
            scope = viewModelScope,
            started = SharingStarted.Lazily // 或 WhileSubscribed
        )
    
  4. 避免在Flow内部进行耗时操作

    // 错误方式
    flow {
        emit(heavyCalculation()) // 阻塞发射
    }
    
    // 正确方式
    flow {
        val result = withContext(Dispatchers.Default) {
            heavyCalculation()
        }
        emit(result)
    }
    

总结:为什么选择SharedFlow处理高频数据?

  1. 数据完整性:缓冲机制确保不丢失重要数据
  2. 高效处理:优化的并发实现支持高吞吐量
  3. 灵活控制:可精细调节的缓冲和溢出策略
  4. 资源友好:智能的背压管理和协程集成
  5. 多订阅支持:真正的发布-订阅模式实现
  • 对于Android开发中的WebSocket、实时位置更新、传感器数据流等高频率数据场景,SharedFlow提供了比LiveData更专业、更可靠的解决方案,能够有效解决数据丢失和性能瓶颈问题。

热流(Hot Flow)与冷流(Cold Flow)

在 Kotlin 协程中,Flow 分为热流(Hot Flow)和冷流(Cold Flow),它们在数据发射行为、订阅机制和使用场景上有显著区别。

一、核心区别对比

特性冷流 (Cold Flow)热流 (Hot Flow)
数据生产时机每次收集时从头开始生产数据数据生产独立于收集存在
多订阅行为每个收集者获得独立完整的数据流所有收集者共享同一数据流
数据共享不共享数据(每次收集重新计算)共享实时数据
典型实现flow { } 构建器SharedFlowStateFlow
资源开销每次收集创建新生产者单生产者多消费者
是否存储数据不存储可配置replay缓存

二、冷流(Cold Flow)深度解析

基本特点

val coldFlow = flow {
    println("开始生产数据")
    emit(1)
    emit(2)
    emit(3)
}

// 第一次收集
coldFlow.collect { println("收集1: $it") } 
// 输出: 开始生产数据 → 收集1:1 → 收集1:2 → 收集1:3

// 第二次收集(重新生产数据)
coldFlow.collect { println("收集2: $it") }
// 输出: 开始生产数据 → 收集2:1 → 收集2:2 → 收集2:3

应用场景

  1. 一次性数据序列:如网络请求响应

    fun fetchUserData(): Flow<User> = flow {
        emit(api.getUser()) // 每次收集都执行新请求
    }
    
  2. 计算密集型操作:需要按需计算的场景

    fun calculateFibonacci(n: Int): Flow<Long> = flow {
        var a = 0L
        var b = 1L
        repeat(n) {
            emit(a)
            val sum = a + b
            a = b
            b = sum
        }
    }
    
  3. 数据库查询:每次需要最新结果的场景

    fun searchProducts(query: String): Flow<List<Product>> = flow {
        emit(dao.searchProducts(query))
    }
    

三、热流(Hot Flow)深度解析

基本特点

val _sharedFlow = MutableSharedFlow<Int>()
val sharedFlow = _sharedFlow.asSharedFlow()

// 生产者
launch {
    (1..3).forEach {
        _sharedFlow.emit(it)
        delay(100)
    }
}

// 消费者1(立即开始收集)
launch {
    sharedFlow.collect { println("消费者1: $it") }
}

// 消费者2(延迟200ms后加入)
launch {
    delay(200)
    sharedFlow.collect { println("消费者2: $it") }
}
/* 可能输出:
   消费者1: 1
   消费者1: 2
   消费者1: 3
   消费者2: 3 (只收到最新值,取决于replay配置)
*/

应用场景

  1. 事件广播:如全局通知、WebSocket消息

    object EventBus {
        private val _events = MutableSharedFlow<Event>()
        val events = _events.asSharedFlow()
        
        fun post(event: Event) {
            _events.tryEmit(event)
        }
    }
    
  2. 状态管理:如UI状态更新

    class MyViewModel {
        private val _state = MutableStateFlow(Loading)
        val state = _state.asStateFlow()
        
        fun loadData() {
            _state.value = Loading
            viewModelScope.launch {
                _state.value = try {
                    Success(api.fetchData())
                } catch(e: Exception) {
                    Error(e)
                }
            }
        }
    }
    
  3. 传感器数据:持续的数据流

    fun observeSensor(): SharedFlow<SensorData> {
        return callbackFlow {
            val listener = object : SensorEventListener {
                override fun onSensorChanged(event: SensorEvent) {
                    trySend(SensorData(event.values))
                }
                // ...
            }
            sensorManager.registerListener(listener, ...)
            awaitClose { sensorManager.unregisterListener(listener) }
        }.shareIn(
            scope = CoroutineScope(Dispatchers.Default),
            started = SharingStarted.WhileSubscribed(5000)
        )
    }
    

四、冷流转热流: shareIn()

通过 shareIn 操作符可将冷流转为热流:

val coldFlow = flow {
    // 模拟高成本数据生产
    emit(expensiveOperation())
}

// 转换为热流
val hotFlow = coldFlow.shareIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(5000), // 无订阅者时保留5秒
    replay = 1 // 新订阅者收到最近1个值
)

SharingStarted 策略:

  • Lazily:第一个订阅者出现时启动,scope取消时停止
  • Eagerly:立即启动,scope取消时停止
  • WhileSubscribed(stopTimeout):有订阅者时活跃,无订阅者后延迟stopTimeout毫秒停止

五、选择指南

使用冷流当:

✅ 数据生产成本高,需要按需计算
✅ 每次收集需要完整独立的数据序列
✅ 数据源是单次操作(如网络请求)

使用热流当:

✅ 需要多订阅者共享实时数据
✅ 处理持续的事件流(如点击事件)
✅ 维护和共享应用状态(如UI状态)
✅ 需要历史数据回溯(replay)

六、性能考量

  1. 冷流注意事项

    • 避免在冷流中执行重复高成本操作
    // 错误示范(每次收集都新建连接)
    flow { emit(api.createExpensiveConnection().getData()) }
    
    // 正确做法
    val data = api.createExpensiveConnection().getData()
    flow { emit(data) }
    
  2. 热流内存管理

    • 合理设置replay缓存大小
    MutableSharedFlow<String>(
        replay = 3,  // 根据业务需求设置
        extraBufferCapacity = 50
    )
    
    • 及时取消不需要的收集以避免内存泄漏
  • 在Android开发中 的使用
    • 冷流用于数据获取
    • 热流用于状态管理和事件通知,两者配合使用能构建高效的数据管道。