Android面试(六):深入Kotlin Flow
🔥 Kotlin Flow核心机制
冷流(Cold Stream)本质剖析
fun buildFlow(): Flow<Int> = flow {
println("开始发射")
emit(1)
emit(2)
}
// 每次collect都会触发全新执行
buildFlow().collect { println(it) } // 输出:开始发射 1 2
buildFlow().collect { println(it) } // 再次输出:开始发射 1 2
Flow如同未通电的流水线,仅当收集者接入时(调用collect
)才会启动生产。这种设计带来两大特性:
- 按需启动:无收集者时不消耗资源
- 独立上下文:每次收集创建全新执行环境
热流(Hot Stream)运作原理
val _state = MutableStateFlow(0)
val state: StateFlow<Int> = _state.asStateFlow()
// 发射端独立运行
launch {
repeat(5) {
_state.value = it
delay(100)
}
}
// 收集端随时接入
launch { state.collect { println("收集器A: $it") } }
delay(200)
launch { state.collect { println("收集器B: $it") } }
StateFlow/SharedFlow如同持续运转的传送带:
- 数据发射与收集者生命周期解耦
- 新收集者默认获取最新状态(StateFlow)或配置范围内的历史数据(SharedFlow)
🚀 高阶操作符实战技巧
事件合并策略对比
// 场景:快速点击防抖
view.clicks()
.debounce(300) // 300ms内仅接收最后一次点击
.collect { processClick() }
// 场景:实时搜索建议
searchInputFlow
.filter { it.length > 2 }
.distinctUntilChanged() // 内容未变更时忽略
.flatMapLatest { query ->
api.search(query).catch { emit(emptyList()) }
}
.collect { showSuggestions(it) }
背压(Backpressure)处理三剑客
// 策略1:缓冲(默认64条)
flow.buffer().collect { /* 生产消费解耦 */ }
// 策略2:丢弃旧值
flow.conflate().collect { /* 仅处理最新值 */ }
// 策略3:手动控制(复杂场景)
channelFlow {
send(1)
offer(2) // 队列满时返回false
}
🧩 StateFlow与SharedFlow架构应用
状态容器最佳实践
class AuthViewModel : ViewModel() {
private val _state = MutableStateFlow(AuthState.IDLE)
val state = _state.asStateFlow()
fun login(username: String, password: String) {
_state.value = AuthState.LOADING
viewModelScope.launch {
try {
authRepo.login(username, password)
_state.value = AuthState.SUCCESS
} catch (e: Exception) {
_state.value = AuthState.ERROR(e)
}
}
}
}
// UI层安全收集
fun observeState() {
lifecycleScope.launchWhenStarted {
viewModel.state.collect { renderUI(it) }
}
}
事件总线替代方案
object EventBus {
private val _events = MutableSharedFlow<Event>(
extraBufferCapacity = 10, // 缓存最近10个事件
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val events = _events.asSharedFlow()
suspend fun post(event: Event) {
_events.emit(event) // 挂起直到有收集者接收
}
}
// 订阅方
EventBus.events
.filterIsInstance<RefreshEvent>()
.collect { updateData() }
🛡️ 异常处理与资源释放
安全收集模式
flow {
emit(data)
}.catch { e ->
// 捕获上游异常
logError(e)
}.onCompletion {
// 无论成功失败都会执行
releaseResources()
}.collect { ... }
超时控制策略
flow {
longRunningProcess()
}.timeout(5.seconds) // 超时抛出TimeCancellationException
.retry(3) { cause ->
cause is TimeoutCancellationException
}
.collect { ... }
⚙️ Flow测试全攻略
虚拟时间测试
@Test
fun testTimerFlow() = runTest {
val flow = tickerFlow(interval = 1.seconds)
val result = mutableListOf<Int>()
val job = launch {
flow.take(3).collect { result.add(it) }
}
advanceTimeBy(3000) // 虚拟时间推进3秒
job.cancel()
assertEquals(listOf(0,1,2), result)
}
状态机验证
@Test
fun testStateTransition() = runTest {
val vm = AuthViewModel(mockRepo)
val states = mutableListOf<AuthState>()
val job = launch { vm.state.collect(states::add) }
vm.login("test", "pass")
advanceUntilIdle() // 等待协程完成
assertEquals(AuthState.LOADING, states[0])
assertTrue(states.last() is AuthState.SUCCESS)
job.cancel()
}
💡 Flow性能优化秘籍
避免常见陷阱
// 反模式:在flow中启动新协程
flow {
launch { // 错误!违反结构化并发
emit(1)
}
}
// 正确方式:使用callbackFlow
callbackFlow {
callbackBasedApi.registerCallback { result ->
trySend(result) // 线程安全发送
}
awaitClose { api.unregister() }
}
调度器选择策略
flow {
// CPU密集型计算
withContext(Dispatchers.Default) {
heavyComputation().forEach { emit(it) }
}
}
.flowOn(Dispatchers.IO) // 影响上游执行上下文
.collect {
// 默认在collect调用方上下文执行
updateUI(it) // 需切换主线程
}
🌐 Flow与响应式编程生态整合
Retrofit适配方案
interface ApiService {
@GET("users")
fun getUsers(): Flow<List<User>> // 自动包装为Flow
}
// 使用方式
api.getUsers()
.catch { emit(emptyList()) }
.collect { showUsers(it) }
Room数据库实时监听
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun observeUsers(): Flow<List<User>>
}
// ViewModel中
val users: Flow<List<User>> = userDao.observeUsers()
.map { it.filter { user -> user.isActive } }
🧪 高级调试技巧
事件日志追踪
flow {
emit(1)
}.onEach { println("发射值: $it") }
.catch { println("捕获异常: $it") }
.launchIn(scope)
调试工具集成
// 添加调试信息
flow {
emit(1)
}.flowWithLifecycle(lifecycle)
.onStart { println("Flow started") }
.onCompletion { println("Flow completed") }
总结
核心
- 冷热流本质:Cold Flow按需生产,Hot Flow持续广播
- 状态管理:StateFlow适合UI状态,SharedFlow处理事件
- 背压策略:buffer/conflate/collectLatest应对不同场景
- 安全收集:lifecycleScope+repeatOnLifecycle避免泄漏
- 测试方案:TestCoroutineScheduler控制虚拟时间轴
💼 高频面试题
StateFlow与LiveData的核心差异?
答:StateFlow必须初始值,LiveData自动解绑,StateFlow支持更丰富的操作符如何避免Flow内存泄漏?
答:使用lifecycleScope.launchWhenStarted
配合repeatOnLifecycle
,或采用flowWithLifecycle
扩展SharedFlow的replay参数作用?
答:配置新订阅者接收的历史数据数量,实现"最近N个事件"的重放Flow如何实现线程切换?
答:上游用flowOn
,下游用withContext
,collect默认在调用方上下文解释flowOn与withContext差异?
答:flowOn影响上游执行上下文,withContext仅改变代码块内上下文