java - 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 类相比,我是否可以简单地在性能方面做到这一点。此外,如果有更好的方法来执行此行为而不是使用协程,请随意建议!
解决方案
使用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事件,我想这是最适合您的情况的解决方案
推荐阅读
- sql - 使用 WITH 子句和 COUNT 子查询进行查询
- openldap - Migrate data from one openldap to another one
- python - 如何使用 OpenCV-Python 检测照片上的黑色轮廓
- c# - 将字符串转换为浮点数对于某些数字但不是其他数字失败
- database - 服务器端预取数据库数据是否存在任何安全风险?
- sql - 如何在 SQLite 中将两列组合在一起?
- python - PyTest 弃用:'junit_family 默认值将更改为'xunit2'
- android - 创建新的 kotlin 项目时无法解决依赖关系
- multithreading - 线程中的 QProcess 有效,但有两种类型的输出错误
- wpf - ArcGIS .NET 从 WebMercator 转换为 Wgs84