How to implement timer with Kotlin coroutines

Rom*_*ych 5 kotlin kotlinx.coroutines

I want to implement timer using Kotlin coroutines, something similar to this implemented with RxJava:

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

It will emit LocalDateTime every new minute.

Jof*_*rey 14

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

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

repeat(10) {
    tickerChannel.receive()
    val currentTime = LocalDateTime.now()
    println(currentTime)
}
Run Code Online (Sandbox Code Playgroud)

如果您需要在“订阅”为每个“滴答”做某事的同时继续进行工作launch,则可以从该频道读取后台协程并做您想做的事情:

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

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

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

  • 在版本“1.3.2”上,ticker 被标记为“ObsoleteCoroutinesApi”,其含义是:“标记协程 API 中**过时**的声明,这意味着相应声明的设计存在严重的已知缺陷,将重新设计它们粗略地说,这些声明将来会被弃用,但目前还没有替代品,所以不能立即弃用。” (10认同)

Ste*_*nke 11

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}")
}

Run Code Online (Sandbox Code Playgroud)

  • 结束时如何通知? (4认同)
  • @Hassa 是延迟创建的 Int 序列。否则,从 0 .. Int.MAX_VALUE 开始的所有 Int 都会立即加载到内存中,这可能是您不希望看到的。 (2认同)

mur*_*glu 8

您可以像这样创建倒数计时器

GlobalScope.launch(Dispatchers.Main) {
            val totalSeconds = TimeUnit.MINUTES.toSeconds(2)
            val tickSeconds = 1
            for (second in totalSeconds downTo tickSeconds) {
                val time = String.format("%02d:%02d",
                    TimeUnit.SECONDS.toMinutes(second),
                    second - TimeUnit.MINUTES.toSeconds(TimeUnit.SECONDS.toMinutes(second))
                )
                timerTextView?.text = time
                delay(1000)
            }
            timerTextView?.text = "Done!"
        }
Run Code Online (Sandbox Code Playgroud)

  • 请改用“lifecycleScope”以避免泄漏 Fragment 或 Activity。 (4认同)

Rap*_*l C 8

另一种可能的解决方案是可重用的 kotlin 扩展 CoroutineScope

fun CoroutineScope.launchPeriodicAsync(
    repeatMillis: Long,
    action: () -> Unit
) = this.async {
    if (repeatMillis > 0) {
        while (isActive) {
            action()
            delay(repeatMillis)
        }
    } else {
        action()
    }
}
Run Code Online (Sandbox Code Playgroud)

然后用作:

var job = CoroutineScope(Dispatchers.IO).launchPeriodicAsync(100) {
  //...
}
Run Code Online (Sandbox Code Playgroud)

然后打断它:

job.cancel()
Run Code Online (Sandbox Code Playgroud)

  • 请记住,后续 `action()` 调用之间的间隔不是定义的 `repeatMillis` 时间,而是 `repeatMillis` + `action()` 执行所需的时间。所以只要 `action()` 不会花费太长时间,这个解决方案就可以。通过使用带有“buffer()”、“conflate()”或“flowOn”的流,我们可以获得大约恒定的间隔。 (3认同)
  • 由于“delay()”调用,这里并不重要,但一般来说,我们应该避免在协程中使用“while (true)”,更喜欢使用“while(isActive)”来正确支持取消。 (2认同)

Dar*_*ini 5

这是使用 Kotlin Flow 的可能解决方案

fun tickFlow(millis: Long) = callbackFlow<Int> {
    val timer = Timer()
    var time = 0
    timer.scheduleAtFixedRate(
        object : TimerTask() {
            override fun run() {
                try { offer(time) } catch (e: Exception) {}
                time += 1
            }
        },
        0,
        millis)
    awaitClose {
        timer.cancel()
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

val job = CoroutineScope(Dispatchers.Main).launch {
   tickFlow(125L).collect {
      print(it)
   }
}

...

job.cancel()
Run Code Online (Sandbox Code Playgroud)

  • 你用协程包装了 Timer,为什么?!这根本没有意义;使用计时器或协程 (4认同)