如何使用Flow协程实现倒计时功能并解决现有实现异常问题
问题原因
- 传参逻辑错误:你给
tickerFlow传入的start是未来10秒的Unix时间戳(通常是10位的大数值),而tickerFlow的逻辑是从start递减到0才停止,相当于要跑几千万秒的循环,自然不会10秒结束,也不会输出预期的倒计时值。 - 倒计时差值计算逻辑错误:
map中LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) - expireDate得到的是当前时间减未来时间,初始值为负数,倒计时过程中会从-10逐渐涨到0,你需要的剩余时间应该用expireDate - 当前时间戳计算。
修正后的实现
1. 倒计时Flow优化
直接传入到期时间戳,内部自动计算剩余时间,避免delay误差累计:
// 入参为到期的秒级Unix时间戳,若后端返回毫秒级可自行调整单位 private fun countDownFlow(expireTimestamp: Long) = flow { var remaining = expireTimestamp - LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) while (remaining >= 0) { emit(remaining) // 直接发射剩余秒数,无需发射Unit delay(1000L) // 每次重新计算剩余时间,避免delay误差导致倒计时不准 remaining = expireTimestamp - LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) } }
2. 调用逻辑简化
val expireDate = LocalDateTime.now().plusSeconds(10L).toEpochSecond(ZoneOffset.UTC) countDownFlow(expireDate) .distinctUntilChanged() // 剩余时间相同自动跳过,无需自定义判断逻辑 .onEach { remainingSeconds -> // 直接用剩余秒数换算时分秒,无需额外获取LocalDateTime val hour = remainingSeconds / 3600 val minute = (remainingSeconds % 3600) / 60 val second = remainingSeconds % 60 val formatText = String.format("%02dh: %02dm: %02ds", hour, minute, second) // 此处更新UI展示即可 } .onCompletion { // 倒计时走到0会自动触发该回调,直接更新结束状态文本 } .launchIn(scope = scope)
补充说明
不需要在emit时返回本地日期时间,直接基于剩余秒数换算时分秒的逻辑性能更好,也不会出现时区转换相关的异常问题。如果后端返回的是毫秒级到期时间,把上述代码中时间戳计算逻辑统一除以1000转成秒级即可。
内容的提问来源于stack exchange,提问作者StuartDTO
相关产品推荐
相关产品推荐

