首页 > 解决方案 > kotlin协程可以像java Timer一样使用吗?

问题描述

我想知道是否可以将 kotlin 协程用作计时器,以便从我的代码中删除最大的 Java 引用。基本上我做了一个例子,我使用 Timer 类来处理计时器显示(毫秒精度)。看(注意 java 导入):

import javax.swing.*
import java.awt.FlowLayout
import java.util.*
import java.util.Timer

class Screen : JFrame() {

  private var label = JLabel("--")
  private var timer: Timer? = null

  init {
    setSize(600, 200)
    defaultCloseOperation = 3
    layout = FlowLayout()
    val bt = JButton("toggle timer")
    bt.addActionListener {
      if (timer == null) {
        val start = System.currentTimeMillis()
        timer = Timer()
        timer!!.scheduleAtFixedRate(object : TimerTask() {
          override fun run() {
            label.text = "elapsed milliseconds: ${System.currentTimeMillis() - start}"
          }
        }, 0, 1)
      } else {
        timer!!.cancel()
        timer = null
      }
    }

    add(label)
    add(bt)
    setLocationRelativeTo(null)
    isVisible = true
  }
}

fun main() {
  Screen()
}

我还尝试用一些协程代码替换经典的 Timer 并得到了这个:

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.awt.FlowLayout
import javax.swing.JButton
import javax.swing.JFrame
import javax.swing.JLabel

fun create(someCallback: () -> Unit) = CoroutineScope(Job()).launch {
  while (true) {
    someCallback()
    delay(1L)
  }
}

class Screen : JFrame() {

  var label = JLabel("--")
  var localJob: Job? = null

  init {
    setSize(600, 200)
    layout = FlowLayout()
    val bt = JButton("toggle timer")
    bt.addActionListener {
      if (localJob == null) {
        val start = System.currentTimeMillis()
        localJob = create { label.text = "elapsed milliseconds: ${System.currentTimeMillis() - start}" }
      } else {
        localJob!!.cancel()
        localJob = null
      }
    }

    add(label)
    add(bt)
    setLocationRelativeTo(null)
    isVisible = true
  }
}

fun main() {
  Screen()
}

我认为这两个代码基本上显示了实现之间的差异。无论如何,我的问题是,与 Timer 类相比,我是否可以简单地在性能方面做到这一点。此外,如果有更好的方法来执行此行为而不是使用协程,请随意建议!

标签: javaandroidkotlinkotlin-coroutines

解决方案


使用flow,注意下面的第一个launch block

import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch

val clicks = MutableStateFlow(false)

suspend fun main(args: Array<String>) {
    coroutineScope {
        launch {
            clicks.withIndex().collectLatest { (idx, value) ->
                if (value) {
                    while (true) {
                        delay(1)
                        println("$idx label.text = ${System.currentTimeMillis()}")
                    }
                }
            }
        }
        launch {
            clicks.value = true
            delay(20)
            clicks.value = false
            delay(20)
            clicks.value = true
            delay(20)
            clicks.value = false
            delay(20)
            clicks.value = true
            delay(20)
            clicks.value = false
            delay(20)
            clicks.value = true
            delay(20)
            clicks.value = false
            delay(20)
        }
    }
}


我不太了解摇摆,当按钮被点击时,切换值并将其设置为点击,现在一些解释

当事件进来时,collectLatest先取消collectLatest中上一次启动的协程,然后用当前值启动一个新的协程,所以每次点击while(true)都会取消之前启动的协程,所以你不必处理cancel事件,我想这是最适合您的情况的解决方案


推荐阅读