Kotlin Flow
基础概念
1. 什么是Flow?
-
定义
- 冷流数据流
- 基于协程的响应式编程组件
- 异步数据流序列
-
特点
- 冷流特性:只有订阅时才会发射数据
- 背压处理:自动处理生产和消费速度不匹配
- 协程支持:完整的协程集成
- 取消支持:可以自动取消
面试题解析
1. Flow的基本组件
- Flow构建器
val flow = flow {
for (i in 1..3) {
delay(100)
emit(i)
}
}- StateFlow
private val _state = MutableStateFlow(0)
val state: StateFlow<Int> = _state.asStateFlow()- SharedFlow
private val _events = MutableSharedFlow<Event>()
val events = _events.asSharedFlow()2. Flow的操作符
- 转换操作符
flow.map { value -> value * 2 }
.filter { value -> value > 0 }
.take(5)- 组合操作符
val flow1 = flowOf(1, 2, 3)
val flow2 = flowOf("a", "b", "c")
flow1.zip(flow2) { number, string ->
"$number$string"
}.collect { println(it) }3. Flow的上下文
- flowOn
flow {
emit(performComputation())
}.flowOn(Dispatchers.Default)
.collect { value ->
updateUI(value)
}- 异常处理
flow {
emit(1)
throw RuntimeException("Error")
}.catch { e ->
emit(-1)
}.collect { value ->
println(value)
}4. StateFlow vs SharedFlow
- StateFlow特点
class ViewModel {
private val _uiState = MutableStateFlow(UiState())
val uiState = _uiState.asStateFlow()
fun updateState() {
_uiState.update { it.copy(loading = true) }
}
}- SharedFlow特点
class EventBus {
private val _events = MutableSharedFlow<Event>(replay = 0)
val events = _events.asSharedFlow()
suspend fun emit(event: Event) {
_events.emit(event)
}
}实战技巧
1. 网络请求处理
fun getUsers(): Flow<List<User>> = flow {
while (true) {
val users = api.getUsers()
emit(users)
delay(5000) // 5秒刷新一次
}
}.catch { e ->
emit(emptyList())
}.flowOn(Dispatchers.IO)2. UI状态管理
data class UiState(
val isLoading: Boolean = false,
val data: List<Item> = emptyList(),
val error: String? = null
)
class MainViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState = _uiState.asStateFlow()
fun loadData() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
try {
val data = repository.getData()
_uiState.update { it.copy(
isLoading = false,
data = data
)}
} catch (e: Exception) {
_uiState.update { it.copy(
isLoading = false,
error = e.message
)}
}
}
}
}3. 事件处理
class EventViewModel : ViewModel() {
private val _events = MutableSharedFlow<UiEvent>()
val events = _events.asSharedFlow()
fun handleAction(action: UserAction) {
viewModelScope.launch {
when (action) {
is UserAction.Click -> _events.emit(UiEvent.ShowToast("Clicked"))
is UserAction.Swipe -> _events.emit(UiEvent.Navigate("next"))
}
}
}
}注意事项
-
性能优化
- 适当使用缓存
- 避免不必要的转换
- 合理使用背压策略
-
内存管理
- 及时取消收集
- 避免内存泄漏
- 使用生命周期感知型收集器
选择题
- Flow的哪个操作符会切换执行上下文? A. map B. flowOn C. filter D. take
答案:B 解析:flowOn操作符用于更改Flow的上游操作的执行上下文。
- 以下哪个Flow类型具有粘性事件特性? A. Flow B. SharedFlow C. StateFlow D. ChannelFlow
答案:C 解析:StateFlow总是保持最新值,新订阅者会立即收到当前值。
问答题
- 解释Flow中的背压是什么,以及如何处理背压问题。
答案:
-
背压概念:
- 生产者和消费者速度不匹配
- 数据积压导致内存问题
- 需要控制数据流速率
-
处理方式:
- buffer:缓冲数据
- conflate:只保留最新值
- collectLatest:取消旧值处理
- sample:采样数据
- 比较Flow和Channel的区别。
答案:
-
Flow特点:
- 冷流,惰性求值
- 支持丰富的操作符
- 适合数据流转换
-
Channel特点:
- 热流,立即执行
- 用于协程间通信
- 类似于队列的概念
编程题
- 实现一个带缓存的网络请求Flow。
class CachedRepository(
private val api: Api,
private val db: Database
) {
fun getData(): Flow<Result<Data>> = flow {
// 先发射缓存数据
val cached = db.getData()
emit(Result.success(cached))
try {
// 请求新数据
val fresh = api.fetchData()
// 更新缓存
db.saveData(fresh)
emit(Result.success(fresh))
} catch (e: Exception) {
emit(Result.failure(e))
}
}.catch { e ->
emit(Result.failure(e))
}.flowOn(Dispatchers.IO)
}
// 使用示例
class ViewModel {
fun loadData() {
viewModelScope.launch {
repository.getData()
.onEach { result ->
when (result) {
is Result.Success -> updateUI(result.data)
is Result.Failure -> showError(result.error)
}
}
.catch { e -> handleError(e) }
.collect()
}
}
}总结
-
核心概念
- Flow基础API
- 操作符使用
- 上下文管理
- 异常处理
-
最佳实践
- 合理使用Flow类型
- 正确处理生命周期
- 优化性能
- 处理背压
-
应用场景
- 网络请求
- UI状态管理
- 事件处理
- 数据转换