Reputation: 955
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)
Upvotes: 58
Views: 93387
Reputation: 870
if you use any background task or background service try this code
val timer = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate({
Log.d("RUNNING ","Thread")
},0,10,TimeUnit.SECONDS)
if you work with UI thers like update UI layout try this code
val timer = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate({
Log.d("RUNNING ","BACKGROUN Thread")
runOnUiThread {
Log.d("RUNNING ","Update UI Thread")
btnUpdate.setText(System.currentTimeMillis().toString())
}
},0,1,TimeUnit.SECONDS)
Upvotes: 3
Reputation: 1
var isActionAchieved = false
var secondsPassed = 0
fun cDTimer(){
if (!isActionAchieved && secondsPassed < 10){ // repeat check if Action NOT Achieved for max of 10 seconds
Handler(Looper.getMainLooper()).postDelayed({
repeatThisFunction()
repeater()
secondsPassed++
}, 1000) //one second till next execution
}
}
fun repeater(){
cDTimer()
}
Upvotes: 0
Reputation: 13302
My solution
viewModelScope.launch(Dispatchers.IO) {
while(isActive) {
when(val response = repository.getApi()) {
is NetworkState.Success -> {
getAllData.postValue(response.data)
}
is NetworkState.Error -> [email protected] = false
}
delay(API_CALL_DELAY)
}
}
Upvotes: 3
Reputation: 964
val timer = object: CountDownTimer(10000, 1000) {
override fun onTick(millisUntilFinished: Long) {
// do something
}
override fun onFinish() {
// do something
}
}
timer.start()
You can also use CountDownTimer for this purpose. As this takes two parameters (the total time and the interval time)
Plus it also provides an on finish method to perform any task when the total time is finished.
Upvotes: 11
Reputation: 31
I'm using recursion with Coroutine its very simple
private fun loop() {
CoroutineScope(IO).launch {
delay(5000)
CoroutineScope(Main).launch {
ManagerToWorker()
loop()
}
}
}
Upvotes: -1
Reputation: 2049
I am calling my function every second like this
val handler = Handler()
handler.postDelayed(object : Runnable {
override fun run() {
//Call your function here
handler.postDelayed(this, 1000)//1 sec delay
}
}, 0)
Upvotes: 4
Reputation: 14173
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()
}
}
}
Upvotes: 87
Reputation: 226
please use
inline fun Timer.schedule(
time: Date,
period: Long,
crossinline action: TimerTask.() -> Unit
): TimerTask
reference: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.concurrent/java.util.-timer/schedule.html
Upvotes: 12
Reputation: 3973
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
Upvotes: 21