269

As the title, is there any way to call a function after delay (1 second for example) in Kotlin?

Nguyen Minh Binh
  • 23,891
  • 30
  • 115
  • 165

15 Answers15

259

There is also an option to use Handler -> postDelayed

 Handler().postDelayed({
                    //doSomethingHere()
                }, 1000)
Bogdan Ustyak
  • 5,639
  • 2
  • 21
  • 16
231

Many Ways

1. Using Handler class

Handler().postDelayed({
    TODO("Do something")
    }, 2000)

2. Using Timer class

Timer().schedule(object : TimerTask() {
    override fun run() {
        TODO("Do something")
    }
}, 2000)

// Shorter

Timer().schedule(timerTask {
    TODO("Do something")
}, 2000)


// Shortest

Timer().schedule(2000) {
    TODO("Do something")
}

3. Using Executors class

Executors.newSingleThreadScheduledExecutor().schedule({
    TODO("Do something")
}, 2, TimeUnit.SECONDS)
Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212
221

You can use Schedule

inline fun Timer.schedule(
    delay: Long, 
    crossinline action: TimerTask.() -> Unit
): TimerTask (source)

example (thanks @Nguyen Minh Binh - found it here: http://jamie.mccrindle.org/2013/02/exploring-kotlin-standard-library-part-3.html)

import java.util.Timer
import kotlin.concurrent.schedule

Timer("SettingUp", false).schedule(500) { 
   doSomething()
}
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Matias Elorriaga
  • 8,880
  • 5
  • 40
  • 58
  • 17
    Thanks! Super easy. Found an example here http://jamie.mccrindle.org/2013/02/exploring-kotlin-standard-library-part-3.html `Timer("SettingUp", false).schedule(500) { doSomething() }` – Nguyen Minh Binh Apr 11 '17 at 14:35
  • 17
    It does compile, if you add these two imports: import java.util.Timer and import kotlin.concurrent.schedule – Customizer Dec 31 '17 at 00:29
  • 3
    @Matias Elorriaga, for me, putting this on a new brand file doesn't compile, even adding the imports Customizer said – Sulfkain Jan 16 '18 at 09:09
  • 3
    you don't need to put it on a file, that method is part of stdlib, follow the link in the first line of the answer, – Matias Elorriaga Jan 16 '18 at 13:38
  • @MatiasElorriaga I think the people want an example! :) – Nolan Amy Sep 07 '18 at 11:17
  • 2
    for those that would like to have it run at an interval: timer.scheduleAtFixedRate(0, inactivityHeartbeat) { //.... do stuff} – Bart Burg Sep 20 '18 at 11:00
  • 4
    I originally thought this wouldn't compile even after importing `kotlin.concurrent.schedule`, because Kotlin merely complained of a signature mismatch, but then I realized I was trying to pass an Int instead of a Long. It compiled after correcting that. – Joe Lapp Apr 30 '19 at 15:19
  • Can someone show this as an example. Cut and pasted this and it does not compile had both imports too. Says the schedule needs a body on the inline function – JPM Jul 18 '19 at 19:46
  • May I know how to cancel the timer? – ロジャー Aug 15 '19 at 14:03
  • Downvoted because it doesn't compile like many of the comments have already indicated. – Johann Aug 24 '19 at 06:35
  • @AndroidDev is part of the standard library, you don’t need to compile, just add the import.. – Matias Elorriaga Aug 24 '19 at 10:56
  • 1
    @IgorGanapolsky - this code compiles fine on Kotlin 1.3.50. What's your error msg? You might have faced my initial problem: make sure the numeric value passed in (500) is a Long and not Int. You'll get a compile error if you do something like: Timer("SettingUp", false).schedule(500+x) {} where "x" is an Int you declared elsewhere. Declare x as a Long and you'll be fine, e.g., val x = 25L – Vahid Pazirandeh Nov 08 '19 at 19:28
  • that work great! Following question is how to insert my class scope inside of this. AKA replace ` doSomething()` with `this.myFunc()`? – Blue Bot Aug 23 '20 at 09:43
  • 1
    Timer creates a new tread, keep this in mind. – Yuriy Chernyshov Jul 29 '21 at 21:36
61

You could launch a coroutine, delay it and then call the function:

 /*GlobalScope.*/launch {
   delay(1000)
   yourFn()
 }

If you are outside of a class or object prepend GlobalScope to let the coroutine run there, otherwise it is recommended to implement the CoroutineScope in the surrounding class, which allows to cancel all coroutines associated to that scope if necessary.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
54

You have to import the following two libraries:

import java.util.*
import kotlin.concurrent.schedule

and after that use it in this way:

Timer().schedule(10000){
    //do something
}
melwil
  • 2,547
  • 1
  • 19
  • 34
jonguer
  • 541
  • 4
  • 3
31
val timer = Timer()
timer.schedule(timerTask { nextScreen() }, 3000)
varghesekutty
  • 997
  • 2
  • 11
  • 23
  • 1
    Can you please explain me why I there's need to write "timerTask" instead of just braces? – Hugo Passos Dec 22 '17 at 07:49
  • 3
    I think you do. `Timer.schedule()` expects a `TimerTask` as it's first argument. `kotlin.concurrent.timerTask()` wraps the given lambda in a `TimerTask` instance. See here: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.concurrent/timer-task.html – Blieque Mar 27 '18 at 17:18
  • Also, the given example can be condensed to one line if the `Timer` object isn't going to be used more than once, e.g., `Timer().schedule(timerTask { ... }, 3000)`. A more Kotlin-friendly option is available too; see jonguer's answer. – Blieque Mar 27 '18 at 17:20
18

If you're using more recent Android APIs the Handler empty constructor has been deprecated and you should include a Looper. You can easily get one through Looper.getMainLooper().

    Handler(Looper.getMainLooper()).postDelayed({
        //Your code
    }, 2000) //millis
dwbrito
  • 5,194
  • 5
  • 32
  • 48
18

If you are in a fragment with viewModel scope you can use Kotlin coroutines:

    myViewModel.viewModelScope.launch {
        delay(2000)
        // DoSomething()
    }
Andy
  • 751
  • 1
  • 12
  • 25
  • what dispatcher do you suggest to use here if the task does not do any api request? – paxcow Dec 23 '22 at 15:25
  • 1
    @paxcow it depends, if its just for a testing use case you can use the Main. But in any other case, I'd always use IO because you are delaying a task, similar what we do with an API request. – Andy Dec 28 '22 at 15:52
  • Only specify dispatchers when you are calling blocking code, or when you're calling main-only code (like interacting with Views or LiveData and you aren't already on Dispatchers.Main). – Tenfour04 Apr 04 '23 at 12:42
12

A simple example to show a toast after 3 seconds :

fun onBtnClick() {
    val handler = Handler()
    handler.postDelayed({ showToast() }, 3000)
}

fun showToast(){
    Toast.makeText(context, "Its toast!", Toast.LENGTH_SHORT).show()
}
Zeero0
  • 2,602
  • 1
  • 21
  • 36
9

If you are looking for generic usage, here is my suggestion:

Create a class named as Run:

class Run {
    companion object {
        fun after(delay: Long, process: () -> Unit) {
            Handler().postDelayed({
                process()
            }, delay)
        }
    }
}

And use like this:

Run.after(1000, {
    // print something useful etc.
})
Ogulcan Orhan
  • 5,170
  • 6
  • 33
  • 49
4

i suggest to use kotlin coroutine and if you want to cancel it. its simple and light weight.

fun repeatFun(): Job {
    return coroutineScope.launch {  
        while(isActive) {
            //do your work here
            delay(1000)
        }
    }
}

//start the loop
val repeatFun = repeatRequest()

//Cancel the loop
repeatFun.cancel()
vignesh
  • 492
  • 3
  • 9
1

I recommended using SingleThread because you do not have to kill it after using. Also, "stop()" method is deprecated in Kotlin language.

private fun mDoThisJob(){

    Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate({
        //TODO: You can write your periodical job here..!

    }, 1, 1, TimeUnit.SECONDS)
}

Moreover, you can use it for periodical job. It is very useful. If you would like to do job for each second, you can set because parameters of it:

Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);

TimeUnit values are: NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS.

ChrisF
  • 134,786
  • 31
  • 255
  • 325
canerkaseler
  • 6,204
  • 45
  • 38
  • You still have to kill it when you navigate away from whatever is using it, or you'll leak the references it is capturing. – Tenfour04 Apr 04 '23 at 12:48
1

I use the following function(s):

fun <T> delayedAsyncTask(delayMs: Long = 0, task: () -> T): ScheduledFuture<T> {
    return Executors
        .newSingleThreadScheduledExecutor()
        .schedule(Callable<T> { task() }, delayMs, TimeUnit.MILLISECONDS)
}

fun <T> asyncTask(task: () -> T) = delayedAsyncTask(0, task)

Here's a unit test for the delayed function. Use of the returned future is optional of course.

    @Test
    fun `delayed task work correctly`() {
        val future = delayedAsyncTask(1000) {
            "lambda returns hello world"
        }
        assertEquals(false, future.isDone)
        Thread.sleep(1100)
        assertEquals(true, future.isDone)
        assertEquals("lambda returns hello world", future.get())
    }
broc.seib
  • 21,643
  • 8
  • 63
  • 62
1

Another way to create a redundant job other than this; that does not require the function to be suspend.

   val repeatableJob = CoroutineScope(Dispatchers.IO).launch {
        while (true) {
            delay(1000)
        }
    }

Cancel when you are done - repeatableJob.cancel()

MDT
  • 1,535
  • 3
  • 16
  • 29
  • Careful using an unmanaged CoroutineScope--this coroutine will leak memory and threads if not manually cancelled when the objects it captures references to become obsolete. It's the same issue GlobalScope has. [More info here](https://elizarov.medium.com/the-reason-to-avoid-globalscope-835337445abc) – Tenfour04 Apr 04 '23 at 12:46
  • I do have suggested cancelling task above and the article just suggests the same manual cancellation or using withContext with suspend function, either way its not a difference with one exception where withContext blocks failure will result in cancellation of all coroutines associated with it, [check this](https://stackoverflow.com/a/56866621/7873768) – MDT Apr 04 '23 at 14:08
  • Yes, you recommended cancelling the Job. I was just adding a point, since using `CoroutineScope().launch` is a common anti-pattern some newbies use to avoid the GlobalScope warning without understanding the warning and when it is appropriate to use GlobalScope. – Tenfour04 Apr 04 '23 at 14:22
  • fair point - there is no lifecycle owner in my way of creating coroutine. So yes manual clean up is mandatory. I do strictly use lifecycles events to cancel or recreate and also sometimes to manually cancel it and re-create when needed this way i have full control for adhoc scenarios. – MDT Apr 04 '23 at 16:26
0

if you are call function in UI.

Timer().schedule(1000) {
  activity?.runOnUiThread {
    doSomething()
  }
}
Infomaster
  • 793
  • 7
  • 8