MST

星途 面试题库

面试题:Kotlin中StateFlow与SharedFlow的基本差异

请阐述Kotlin中StateFlow和SharedFlow在概念、特性以及使用场景上的基本差异。并举例说明在何种情况下你会优先选择使用StateFlow,何种情况下会选择SharedFlow。
21.0万 热度难度
编程语言Kotlin

知识考点

AI 面试

面试题答案

一键面试

概念差异

  • StateFlow:是一个状态持有者,它可以持有一个状态值,并将该值的变化通知给它的收集器。StateFlow始终需要一个初始值,因为它代表了一个状态,即使在没有数据更新时,也有一个默认状态。
  • SharedFlow:是一个热流,它可以将多个值发送给多个收集器。它并不持有状态,只是负责发送数据。

特性差异

  • StateFlow
    • 初始值:必须有初始值。
    • 一致性:收集器总是能收到最新的值,即使在订阅时新值还未产生,也会收到初始值。
    • 重放:会向新的收集器重放最后一个值。
  • SharedFlow
    • 初始值:不需要初始值。
    • 一致性:新的收集器不会自动收到之前的值,除非配置了重放缓存。
    • 重放:可以通过配置重放缓存大小,决定向新收集器重放多少个之前的值。

使用场景差异

  • 优先选择StateFlow的场景
    • 当需要表示一个状态时,例如界面的加载状态(loading、success、error),因为StateFlow始终有一个当前状态值。
    • 当希望收集器总是能获取到最新状态,即使在订阅时该状态未改变,比如用户登录状态,新订阅者应立即知道当前登录状态。
    • 示例代码:
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

class LoginViewModel {
    private val _isLoggedIn = MutableStateFlow(false)
    val isLoggedIn: StateFlow<Boolean> = _isLoggedIn

    fun login() {
        // 模拟登录操作
        _isLoggedIn.value = true
    }
}

fun main() = runBlocking {
    val viewModel = LoginViewModel()
    launch {
        viewModel.isLoggedIn.collect { isLoggedIn ->
            println("Is logged in: $isLoggedIn")
        }
    }
    viewModel.login()
}
  • 优先选择SharedFlow的场景
    • 当需要发送事件流,且不需要收集器接收历史事件,例如按钮点击事件,新订阅者不需要知道之前的点击情况。
    • 当希望控制向新收集器重放多少个历史值时,比如实时聊天消息,可配置重放缓存让新订阅者获取最近几条消息。
    • 示例代码:
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

class ButtonClickViewModel {
    private val _clickFlow = MutableSharedFlow<Unit>()
    val clickFlow: SharedFlow<Unit> = _clickFlow

    fun onButtonClick() {
        launch {
            _clickFlow.emit(Unit)
        }
    }
}

fun main() = runBlocking {
    val viewModel = ButtonClickViewModel()
    launch {
        viewModel.clickFlow.collect {
            println("Button clicked")
        }
    }
    viewModel.onButtonClick()
}