Kotlin Flow 的使用

我来详细介绍 Kotlin Flow 的使用。Flow 是 Kotlin 协程库中的响应式流处理工具,用于处理异步数据流。

1. 基础概念

创建 Flow

kotlin 复制代码
// 方式1: 使用 flow 构建器
fun simpleFlow(): Flow<Int> = flow {
    for (i in 1..3) {
        delay(100)
        emit(i) // 发射值
    }
}

// 方式2: 使用 asFlow() 扩展
val numberFlow = (1..5).asFlow()

// 方式3: 使用 flowOf
val stringFlow = flowOf("A", "B", "C")

收集 Flow

kotlin 复制代码
fun main() = runBlocking {
    simpleFlow().collect { value ->
        println(value)
    }
}
// 输出: 1, 2, 3 (每隔100ms)

2. 操作符

转换操作符

kotlin 复制代码
fun transformExample() = runBlocking {
    (1..3).asFlow()
        .map { it * it } // 转换每个元素
        .filter { it % 2 == 1 } // 过滤
        .collect { println(it) }
    // 输出: 1, 9
}

transform 操作符

kotlin 复制代码
fun transformExample() = runBlocking {
    (1..3).asFlow()
        .transform { value ->
            if (value % 2 == 0) {
                emit("Even: $value")
                emit("Square: ${value * value}")
            }
        }
        .collect { println(it) }
}

限长操作符

kotlin 复制代码
fun takeExample() = runBlocking {
    (1..10).asFlow()
        .take(3) // 只取前3个
        .collect { println(it) }
    // 输出: 1, 2, 3
}

3. 终端操作符

kotlin 复制代码
fun terminalOperators() = runBlocking {
    val sum = (1..5).asFlow()
        .reduce { accumulator, value -> accumulator + value }
    println("Sum: $sum") // 输出: Sum: 15
    
    val first = (1..5).asFlow().first()
    println("First: $first") // 输出: First: 1
    
    val last = (1..5).asFlow().last()
    println("Last: $last") // 输出: Last: 5
}

4. 上下文和异常处理

上下文切换

kotlin 复制代码
fun contextExample() = runBlocking {
    flow {
        for (i in 1..3) {
            Thread.sleep(100)
            emit(i)
        }
    }
    .flowOn(Dispatchers.Default) // 指定上游执行的上下文
    .collect { value ->
        println("Received $value on ${Thread.currentThread().name}")
    }
}

异常处理

kotlin 复制代码
fun exceptionHandling() = runBlocking {
    flow {
        emit(1)
        throw RuntimeException("Error")
    }
    .catch { e -> 
        println("Caught exception: $e")
        emit(-1) // 在异常时发射备用值
    }
    .onCompletion { cause -> 
        println("Flow completed ${cause?.message ?: "successfully"}")
    }
    .collect { println(it) }
}

5. 组合多个 Flow

zip 操作符

kotlin 复制代码
fun zipExample() = runBlocking {
    val nums = (1..3).asFlow()
    val strs = flowOf("One", "Two", "Three")
    
    nums.zip(strs) { a, b -> "$a -> $b" }
        .collect { println(it) }
    // 输出: 1 -> One, 2 -> Two, 3 -> Three
}

combine 操作符

kotlin 复制代码
fun combineExample() = runBlocking {
    val numbers = (1..3).asFlow().onEach { delay(300) }
    val letters = flowOf("A", "B", "C").onEach { delay(400) }
    
    numbers.combine(letters) { number, letter -> "$number$letter" }
        .collect { println(it) }
    // 输出组合结果,基于最新的值
}

6. 扁平流操作符

flatMapConcat

kotlin 复制代码
fun flatMapConcatExample() = runBlocking {
    (1..3).asFlow()
        .flatMapConcat { value ->
            flow {
                emit("First $value")
                emit("Second $value")
            }
        }
        .collect { println(it) }
    // 顺序输出: First 1, Second 1, First 2, Second 2, First 3, Second 3
}

flatMapMerge

kotlin 复制代码
fun flatMapMergeExample() = runBlocking {
    (1..3).asFlow()
        .flatMapMerge { value ->
            flow {
                delay((4 - value) * 100L) // 倒序延迟
                emit("Value $value")
            }
        }
        .collect { println(it) }
    // 可能输出: Value 3, Value 2, Value 1 (并发执行)
}

7. 实际应用示例

网络请求 + 数据库操作

kotlin 复制代码
class UserRepository {
    fun getUsers(): Flow<List<User>> = flow {
        // 从网络获取数据
        val remoteUsers = apiService.getUsers()
        // 保存到数据库
        userDao.insertUsers(remoteUsers)
        // 从数据库观察变化
        emitAll(userDao.observeUsers())
    }.flowOn(Dispatchers.IO)
    
    fun getUserWithDetails(userId: String): Flow<UserWithDetails> {
        return userDao.observeUser(userId)
            .flatMapLatest { user ->
                when {
                    user == null -> flowOf(null)
                    user.details == null -> {
                        // 如果缺少详情,从网络获取
                        val details = apiService.getUserDetails(userId)
                        userDao.updateUserDetails(userId, details)
                        userDao.observeUserWithDetails(userId)
                    }
                    else -> userDao.observeUserWithDetails(userId)
                }
            }
    }
}

搜索功能

kotlin 复制代码
class SearchViewModel : ViewModel() {
    private val searchQuery = MutableStateFlow("")
    
    val searchResults: Flow<List<SearchResult>> = searchQuery
        .debounce(300) // 防抖,300ms 内只取最后一次
        .distinctUntilChanged() // 去重
        .filter { it.length >= 2 } // 过滤短查询
        .flatMapLatest { query ->
            if (query.isBlank()) {
                flowOf(emptyList())
            } else {
                searchRepository.search(query)
                    .catch { e -> 
                        emit(emptyList())
                        // 可以在这里处理错误
                    }
            }
        }
        .flowOn(Dispatchers.Default)
    
    fun onSearchQueryChanged(query: String) {
        searchQuery.value = query
    }
}

8. StateFlow 和 SharedFlow

StateFlow (状态容器)

kotlin 复制代码
class CounterViewModel : ViewModel() {
    private val _count = MutableStateFlow(0)
    val count: StateFlow<Int> = _count.asStateFlow()
    
    fun increment() {
        _count.value++
    }
}

// 在 UI 中观察
fun observeCounter() {
    viewModel.count.collect { count ->
        updateCounterUI(count)
    }
}

SharedFlow (事件总线)

kotlin 复制代码
object EventBus {
    private val _events = MutableSharedFlow<Event>()
    val events = _events.asSharedFlow()
    
    suspend fun sendEvent(event: Event) {
        _events.emit(event)
    }
}

// 发送事件
EventBus.sendEvent(UserLoggedInEvent(user))

// 接收事件
EventBus.events
    .filterIsInstance<UserLoggedInEvent>()
    .collect { event ->
        // 处理登录事件
    }

关键特点总结

  1. 冷流:Flow 是冷流,有收集者时才开始执行
  2. 结构化并发:与协程深度集成
  3. 背压处理:自动处理生产者和消费者的速度不匹配
  4. 丰富的操作符:提供函数式编程风格的操作符
  5. 线程安全:通过 flowOn 安全切换上下文

Flow 非常适合处理异步数据流,特别是在 Android 开发、后端 API 调用等场景中。

相关推荐
一只小灿灿1 分钟前
深入解析 Maven 与 Gradle:Java 项目构建工具的安装、使用
java·开发语言·maven
树在风中摇曳5 分钟前
C语言 | 文件操作详解与实战示例
c语言·开发语言
程序员卷卷狗21 分钟前
MySQL 慢查询优化:从定位、分析到索引调优的完整流程
android·mysql·adb
njsgcs31 分钟前
excel提取长宽,进行排版导出ezdxf 装箱算法 贪婪 总利用率91%
开发语言·python·excel
写点啥呢35 分钟前
Android Studio 多语言助手插件:让多语言管理变得简单高效
android·ai·ai编程·多语言
lsx20240638 分钟前
Memcached stats sizes 命令详解
开发语言
吃不饱的得可可42 分钟前
C++17常用新特性
开发语言·c++
西游音月1 小时前
(4)框架搭建:Qt实战项目之主窗体介绍
开发语言·qt
leo__5201 小时前
MATLAB实现图像超分辨率方法
开发语言·matlab