![](/img/trans.png)
[英]Unable to create converter for kotlinx.coroutines.flow.Flow, No JsonAdapter for kotlinx.coroutines.flow.Flow
[英]How to create a countdown with flow coroutines
我正在尝试使用协程创建一个流程,但它并没有给我预期的结果。 我想要的是在时间到达 0 时给出一个过期时间(不管它是毫秒、秒等),它会停止倒计时。 我现在拥有的是:
private fun tickerFlow(start: Long, end: Long = 0L) = flow {
var count = start
while (count >= end) {
emit(Unit)
count--
delay(1_000L)
}
}
然后我把这个 function 称为:
val expireDate = LocalDateTime.now().plusSeconds(10L).toEpochSecond(ZoneOffset.UTC)
tickerFlow(expireDate)
.map { LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) - expireDate }
.distinctUntilChanged { old, new ->
old == new
}
.onEach {
//Here I should print the timer going down with this pattern
//00h: 00m: 00s I did it with String.format("%02dh: %02dm: %02ds") and it works though.
}
.onCompletion {
//Setting the text when completed
}
.launchIn(scope = scope)
但即使通过这个测试,我正在尝试将到期时间设置为从现在开始的 10 秒,它不会像我想要的那样打印或结束。 我错过了什么吗? 有什么办法可以发出本地日期时间,所以我有小时、分钟和秒? 也许我必须做微积分才能从milis / seconds获得秒,分钟小时。
TLDR;
我从后端得到一个到期日期,我想知道这个到期日期什么时候结束,所以我必须用 now() 计算它并检查它什么时候到期。
你真的不需要这里的流量。 试试这个代码:
val expireDate = LocalDateTime.now().plusSeconds(10L).toEpochSecond(ZoneOffset.UTC)
val currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
for (i in (expireDate - currentTime) downTo 1) {
println("$i seconds remaining") // Format the remaining seconds however you wish
delay(1_000) // Delay for 1 second
}
println("TIME UP") // Run your completion code here
此外,此代码可以安全地在主线程上运行,因为delay
不会阻塞。
在您的代码中,问题在于您将expireDate
本身传递给tickerFlow
。 expireDate
包含从纪元开始的以秒为单位的时间,而不是与当前时间的秒差。 只需将expireDate - LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
传递给tickerFlow,它就会起作用。
编辑:使用流程完成实现
private fun tickerFlow(start: Long, end: Long = 0L) = flow {
for (i in start downTo end) {
emit(i)
delay(1_000)
}
}
val expireDate = LocalDateTime.now().plusSeconds(10L).toEpochSecond(ZoneOffset.UTC)
val currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
tickerFlow(expireDate - currentTime)
.onEach { secondsRemaining ->
// Format and display the time
}
.onCompletion {
// Handle completion
}
.launchIn(scope)
有flow
的解决方案
private fun countDownFlow(
start: Long,
delayInSeconds: Long = 1_000L,
) = flow {
var count = start
while (count >= 0L) {
emit(count--)
delay(delayInSeconds)
}
}
然后给定一个到期日期,将当前日期减去它们并将其作为开始传递。
val expireDate = LocalDateTime.now().plusSeconds(10L).toEpochSecond(ZoneOffset.UTC)
val currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
tickerFlow(expireDate - currentTime)
.onEach {
binding.yourTimer.text = String.format(
"%02dh: %02dm: %02ds",
TimeUnit.SECONDS.toHours(it),
TimeUnit.SECONDS.toMinutes(it),
TimeUnit.SECONDS.toSeconds(it),
)
}
.onCompletion {
//Update UI
}
.launchIn(coroutineScope)
如果赶时间复制/粘贴这个!
fun getFlow( delayTimeMilliseconds: Long, startValue: Long, stepValue : Long = 1, endValue : Long =0): Flow<Long> =
( startValue downTo endValue step stepValue ).asFlow().flowOn(Dispatchers.IO)
.onEach { delay( delayTimeMilliseconds) }
.onStart { emit( startValue) }
.conflate()
.transform { remainingValue: Long ->
if(remainingValue<0) emit(0)
else emit( remainingValue)
}
在我的生产应用程序中,我根据干净的架构使用计时器作为用例。 像这样:
class TimerFlowUseCase constructor() : StateFlowUseCase<TimerFlowUseCase.Params, Long>() {
override suspend fun getFlow(params: Params): Flow<Long> =
(params.startValue downTo params.endValue step params.stepValue ).asFlow().flowOn(Dispatchers.IO)
.onEach { delay(params.delayTimeMilliseconds) }
.onStart { emit(params.startValue) } // Emits total value on start
.conflate()
.transform { remainingValue: Long ->
if(remainingValue<0) emit(0)
else emit( remainingValue)
}
data class Params( val delayTimeMilliseconds: Long, val startValue: Long, val stepValue : Long = 1, val endValue : Long =0)
}
超类在哪里:
abstract class StateFlowUseCase<P, R> {
suspend operator fun invoke(params: P , coroutineScope: CoroutineScope): StateFlow<R> {
return getFlow(params).stateIn(coroutineScope)
}
abstract suspend fun getFlow(params: P): Flow<R>
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.