Lucas Sousa
Lucas Sousa

Reputation: 352

Can kotlin coroutines be used like java Timer?

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

Answers (2)

钵钵鸡实力代购
钵钵鸡实力代购

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

Marko Topolnik
Marko Topolnik

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

Related Questions