Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin: call a function every second

Tags:

android

kotlin

I want to create a simple countdown for my game, when the game starts I want this function to be called every second:

fun minusOneSecond(){   if secondsLeft > 0{      secondsLeft -= 1      seconds_thegame.text = secondsLeft.toString()   } } 

I tried this:

var secondsLeft = 15  timer.scheduleAtFixedRate(    object : TimerTask() {        override fun run() {          minusOneSecond()       }      },0, 1000 )   // 1000 Millisecond  = 1 second 

But the app unfortunately stops, the 2nd time the run function is called

I just started with android development and Kotlin 3 weeks ago and so far I understand the most out of it.

With swift in Xcode I use this line and I thought something similar would work with Kotlin

setTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(minusOneSecond), userInfo: nil, repeats: true) 
like image 560
Alex Karapanos Avatar asked Apr 08 '19 10:04

Alex Karapanos


People also ask

How do you call a function after some time in Kotlin?

Timer import kotlin. concurrent. schedule fun main(args: Array<String>) { // Execution starting point println("Hello world!!") // Delay of 5 sec Timer(). schedule(5000){ //calling a function newMethod() } } fun newMethod(){ println("Delayed method call!") }

How do you use kotlin handler postDelayed?

The postDelayed method takes two parameters Runnable and delayMillis . It adds the Runnable to the thread's message queue to be run after the specified amount of time elapses. The Runnable will execute on the thread to which this handler is attached.

How do you use kotlin runnable?

You can use Handler to add a Runnable object or messages to the Looper to execute the code on the thread associated with the Looper. Android associates each Handler instance with a single thread and that thread's message queue. Whenever you create a new Handler instance, it is tied up to a single Looper .


2 Answers

Problem: Timer class uses a background thread with a queue to queue and execute all tasks sequentially. From your code, because you update UI (changing TextView content in minusOneSecond function). That why the app throws the following exception and make your app crash.

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Solution: There are many ways to achieve your task, but I prefer using post() and postDelayed() method from Handler class. Because it's simple and easy to understand.

val mainHandler = Handler(Looper.getMainLooper())  mainHandler.post(object : Runnable {     override fun run() {         minusOneSecond()         mainHandler.postDelayed(this, 1000)     } }) 

Update: From author's comment about how to pause/resume the task from Handler. Here is an example.

class MainActivityKt : AppCompatActivity() {      lateinit var mainHandler: Handler      private val updateTextTask = object : Runnable {         override fun run() {             minusOneSecond()             mainHandler.postDelayed(this, 1000)         }     }      override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)         setContentView(R.layout.activity_main)         // Your logic code         ...         mainHandler = Handler(Looper.getMainLooper())     }      override fun onPause() {         super.onPause()         mainHandler.removeCallbacks(updateTextTask)     }      override fun onResume() {         super.onResume()         mainHandler.post(updateTextTask)     }      fun minusOneSecond() {         if secondsLeft > 0 {             secondsLeft -= 1             seconds_thegame.text = secondsLeft.toString()         }     } } 
like image 192
Son Truong Avatar answered Sep 28 '22 04:09

Son Truong


I am using this code to update a clock every minute

 fixedRateTimer("timer", false, 0L, 60 * 1000) {      [email protected] {          tvTime.text = SimpleDateFormat("dd MMM - HH:mm", Locale.US).format(Date())      }  } 

so you have to run it with paratemer 1000 instead of 60*1000

like image 45
gmetax Avatar answered Sep 28 '22 03:09

gmetax