如何使用 Kotlin 协程实现计时器

新手上路,请多包涵

我想使用 Kotlin 协程实现计时器,类似于使用 RxJava 实现的:

        Flowable.interval(0, 5, TimeUnit.SECONDS)
                    .observeOn(AndroidSchedulers.mainThread())
                    .map { LocalDateTime.now() }
                    .distinctUntilChanged { old, new ->
                        old.minute == new.minute
                    }
                    .subscribe {
                        setDateTime(it)
                    }

它将每隔一分钟发出 LocalDateTime。

原文由 Roman Nazarevych 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.6k
2 个回答

编辑:请注意,原始答案中建议的 API 现在标记为 @ObsoleteCoroutineApi

Ticker 通道目前未与结构化并发集成,它们的 api 将来会发生变化。

您现在可以使用 Flow API 创建您自己的代码流:

 import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun tickerFlow(period: Duration, initialDelay: Duration = Duration.ZERO) = flow {
    delay(initialDelay)
    while (true) {
        emit(Unit)
        delay(period)
    }
}

您可以以与当前代码非常相似的方式使用它:

 tickerFlow(5.seconds)
    .map { LocalDateTime.now() }
    .distinctUntilChanged { old, new ->
        old.minute == new.minute
    }
    .onEach {
        setDateTime(it)
    }
    .launchIn(viewModelScope) // or lifecycleScope or other

注意:使用此处编写的代码, tickerFlow 未考虑处理元素所花费的时间,因此延迟可能不规则(这是元素处理 之间 的延迟)。如果您希望自动收报机独立于每个元素的处理进行计时,您可能需要使用 缓冲区 或专用线程(例如通过 flowOn )。


原答案

我相信它仍处于试验阶段,但您可以使用 TickerChannel 每隔 X 毫秒生成一次值:

 val tickerChannel = ticker(delayMillis = 60_000, initialDelayMillis = 0)

repeat(10) {
    tickerChannel.receive()
    val currentTime = LocalDateTime.now()
    println(currentTime)
}

如果你需要继续做你的工作,而你的“订阅”为每个“滴答”做一些事情,你可以 launch 一个将从这个频道读取并做你想做的事情的后台协程:

 val tickerChannel = ticker(delayMillis = 60_000, initialDelayMillis = 0)

launch {
    for (event in tickerChannel) {
        // the 'event' variable is of type Unit, so we don't really care about it
        val currentTime = LocalDateTime.now()
        println(currentTime)
    }
}

delay(1000)

// when you're done with the ticker and don't want more events
tickerChannel.cancel()

如果你想从循环内部停止,你可以简单地跳出它,然后取消通道:

 val ticker = ticker(500, 0)

var count = 0

for (event in ticker) {
    count++
    if (count == 4) {
        break
    } else {
        println(count)
    }
}

ticker.cancel()

原文由 Joffrey 发布,翻译遵循 CC BY-SA 4.0 许可协议

Kotlin Flows 的一个非常实用的方法可能是:

 // Create the timer flow
val timer = (0..Int.MAX_VALUE)
    .asSequence()
    .asFlow()
    .onEach { delay(1_000) } // specify delay

// Consume it
timer.collect {
    println("bling: ${it}")
}

原文由 Steffen Funke 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏