如何创建订阅支持延迟启动的Kotlin Flow,实现登录令牌循环刷新
实现方案
核心思路是通过状态流控制刷新逻辑的启停,配合Kotlin Flow的flatMapLatest操作符实现条件触发,同时保留Android生命周期绑定的自动停止特性。
1. 改造刷新令牌UseCase
保留原有冷流实现,仅做小的稳定性优化:
RefreshTokenUseCase.kt class RefreshTokenUseCase { // 冷流特性:只有被收集时才会执行内部循环逻辑 fun tokenRefresh(pollingIntervalMs: Long = 5000): Flow<RefreshResult> = flow { // 用协程上下文活跃状态判断替代while(true),更安全 while (currentCoroutineContext().isActive) { delay(pollingIntervalMs) val res = refreshToken(userToken) emit(res) } } }
2. 实现ViewModel控制逻辑
新增状态开关控制刷新启停,通过流变换实现逻辑解耦:
SomeViewModel.kt class SomeViewModel( private val refreshTokenUseCase: RefreshTokenUseCase ) : ViewModel() { // 刷新功能开关,初始为关闭状态 private val _isRefreshEnabled = MutableStateFlow(false) // 对外暴露的登出错误流 val loggedOutError: Flow<RefreshResult> = _isRefreshEnabled // 开关状态变更时切换对应的流 .flatMapLatest { isEnabled -> if (isEnabled) { // 开关打开时,启动刷新流并过滤过期错误 refreshTokenUseCase.tokenRefresh() .filter { it == TokenExpired } } else { // 开关关闭时返回空流,不产生任何事件 emptyFlow() } } // 配置为共享流,避免配置变更后重复消费事件 .shareIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), replay = 0 ) fun userHasLoggedIn() { // 打开刷新开关,自动启动刷新逻辑 _isRefreshEnabled.update { true } } // 可选扩展:登出时关闭刷新 fun userHasLoggedOut() { _isRefreshEnabled.update { false } } }
3. 视图层收集逻辑
推荐用官方更推荐的repeatOnLifecycle替代launchWhenStarted,避免后台回调问题:
SomeFragment.kt override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { // 仅在视图处于STARTED及以上生命周期时收集流 repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.loggedOutError.collect { showLoggedOutError() } } } }
方案优势
- 启动时机可控:初始化阶段刷新逻辑完全休眠,只有主动调用
userHasLoggedIn后才会启动刷新 - 自动生命周期管理:视图退到后台时自动停止收集,刷新协程自动取消,回到前台后自动恢复
- 无手动协程管理:所有逻辑通过Flow原生操作符实现,避免手动维护Job导致的内存泄漏或重复执行问题
- 边界处理完善:登出时只要关闭开关即可立刻停止刷新,多次调用启动方法不会重复创建刷新任务
内容的提问来源于stack exchange,提问作者Daniel Dudek
相关产品推荐
相关产品推荐

