Reputation: 352
I'm wondering if I can use kotlin coroutines as a Timer in order to remove the maximum Java references from my code. Basically I made a example where I use the class Timer to handle a chronometer display (milliseconds precision). Look (note to the java imports):
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()
}
I also tried replace the classic Timer by some coroutines code and got this:
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()
}
I think the two codes shows basically the differences between the implementations. Anyway, my questions is if I can simply do this in terms of performance in comparison to Timer class. Also, if there's a better way to do this behavior instead of using coroutines feel frfee to suggest!
Upvotes: 2
Views: 1140
Reputation: 1042
use flow , pay attention to the first launch block below
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)
}
}
}
I dont know much about swing, when button get click, toggle the value and set it to clicks, now some explanation
when event come in, collectLatest
will first cancel the last launched coroutine within collectLatest and then launch a new one with current value, so every time you click, previous launched while(true)
is cancelled,so you dont have to deal with the cancel event, i think this is the most neatly solution for your case
Upvotes: 2
Reputation: 200206
With or without coroutines, you shouldn't use a timer that creates its own thread. Swing has javax.swing.Timer
, so you'd use that. You can also launch a coroutine inside Dispatchers.Swing
(from the kotlinx-coroutines-swing
artifact) and use a loop like you have, but I'd prefer to use the native timer even if I was using coroutines because I'd trust its timing more.
Upvotes: 1