代码之家  ›  专栏  ›  技术社区  ›  Roman Nazarevych

如何用Kotlin协程实现定时器

  •  0
  • Roman Nazarevych  · 技术社区  · 7 年前

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

    它将在每一分钟发出LocalDateTime。

    0 回复  |  直到 6 年前
        1
  •  21
  •   Joffrey    6 年前

    我相信这仍然是实验性的,但是你可以用 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()
    
        2
  •  3
  •   Benjamin Ledet    6 年前

    编辑: Joffrey 用更好的方法编辑了他的解决方案。

    旧的:

    的解决方案对我有效,但我遇到了for循环的问题。

    我必须像这样取消for循环中的ticker:

                val ticker = ticker(500, 0)
                for (event in ticker) {
                    if (...) {
                        ticker.cancel()
                    } else {
                        ...
                        }
                    }
                }
    

    但是 ticker.cancel() 正在引发cancellationException,因为for循环一直在跟踪此操作。

    我不得不使用while循环来检查通道是否未关闭,以避免出现此异常。

                    val ticker = ticker(500, 0)
                    while (!ticker.isClosedForReceive && ticker.iterator().hasNext()) {
                        if (...) {
                            ticker.cancel()
                        } else {
                            ...
                            }
                        }
                    }
    
        3
  •  3
  •   murgupluoglu Josef Pfleger    6 年前

    你可以创建这样的倒计时

    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!"
            }
    
        4
  •  0
  •   Bassam Helal    6 年前

    它没有使用Kotlin协同程序,但是如果您的用例足够简单,那么您可以使用 fixedRateTimer 或 timer docs here )解析为JVM本机 Timer

    interval 重要的 性能和内存改进。

    View.post()

    唯一真正的烦恼是,你需要自己跟踪旧时代的状态,而不是依赖RxJava来帮你完成它。

    但这总是会更快(如果你在做一些性能关键的事情,比如UI动画等,这一点很重要),而且不会有RxJava的可流动性所带来的内存开销。

    固定定时器

    
    var currentTime: LocalDateTime = LocalDateTime.now()
    
    fixedRateTimer(period = 5000L) {
        val newTime = LocalDateTime.now()
        if (currentTime.minute != newTime.minute) {
            post { // post the below code to the UI thread to update UI stuff
                setDateTime(newTime)
            }
            currentTime = newTime
        }
    }