Как вызвать функцию после задержки в Котлине?

173

Как видно из заголовка, есть ли способ вызвать функцию после задержки (например, 1 секунда) Kotlin?

Нгуен Минь Бинь
источник

Ответы:

146

Вы можете использовать расписание

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

пример (спасибо @Nguyen Minh Binh - нашел здесь: 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()
}
Матиас Элорриага
источник
16
Благодарность! Очень просто. Нашел пример здесь jamie.mccrindle.org/2013/02/… Timer("SettingUp", false).schedule(500) { doSomething() }
Нгуен Минь Бинь
11
Он компилируется, если вы добавите эти два импорта: import java.util.Timer и import kotlin.concurrent.schedule
Customizer
3
@Matias Elorriaga, для меня размещение этого в новом фирменном файле не компилируется, даже добавление импорта, сказал
Настройщик,
3
вам не нужно помещать его в файл, этот метод является частью stdlib, перейдите по ссылке в первой строке ответа,
Матиас Элорриага
3
Первоначально я думал, что это не будет компилироваться даже после импорта kotlin.concurrent.schedule, потому что Kotlin просто пожаловался на несоответствие подписи, но затем я понял, что пытаюсь передать Int вместо Long. Он скомпилирован после исправления этого.
Джо Лапп
195

Также есть возможность использовать Handler -> postDelayed

 Handler().postDelayed({
                    //doSomethingHere()
                }, 1000)
Богдан Устяк
источник
20
Пожалуйста, добавьте, что он доступен только на Android, так как вопрос касается общего метода котлина (хотя у него есть тег Android)
Йоав Штернберг,
5
С вашей стороны это неконструктивно. В результате, когда пользователи будут искать тег Android, они могут подумать, что это неправильный ответ.
Богдан Устяк
11
Для Android лучше использовать Handler, чем Timer: stackoverflow.com/questions/20330355/timertask-or-handler
woprandi
Думаю, стоит добавить код для удаления обработчиков после завершения активности / фрагмента.
CoolMind
1
Это не будет выполняться в потоке пользовательского интерфейса, если вы намеревались это сделать.
AndroidDev
115

Много способов

1. Использование Handlerкласса

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

2. Использование Timerкласса

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

Короче

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

Самый короткий

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

3. Использование Executorsкласса

Executors.newSingleThreadScheduledExecutor().schedule({
    TODO("Do something")
}, 2, TimeUnit.SECONDS)
Хемрадж
источник
1
и какое, по-вашему, здесь лучшее решение?
Тамим Аттафи
2
Вероятно, первый, использующий обработчик. См stackoverflow.com/a/40339630/1159930
Markymark
40

Вам необходимо импортировать следующие две библиотеки:

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

и после этого используйте его так:

Timer().schedule(10000){
    //do something
}
Jonguer
источник
32

Вы можете launchсопрограмму, delayа затем вызвать функцию:

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

Если вы находитесь за пределами класса или объекта, добавьте GlobalScopeк нему возможность запуска сопрограммы, в противном случае рекомендуется реализовать CoroutineScopeв окружающем классе, что позволяет при необходимости отменить все сопрограммы, связанные с этой областью.

Йонас Вильмс
источник
Благодарность! Странно, что сопрограммы упоминались только в 2018 году.
CoolMind
@coolMind, они стабильны уже несколько месяцев, так что они совсем новые ...
Джонас
Да, с октября по ноябрь, но был раньше.
CoolMind
26
val timer = Timer()
timer.schedule(timerTask { nextScreen() }, 3000)
Varghesekutty
источник
1
Не могли бы вы объяснить мне, почему мне нужно писать "timerTask" вместо фигурных скобок?
Уго Пассос
3
Я думаю, что да. Timer.schedule()ожидает в TimerTaskкачестве первого аргумента. kotlin.concurrent.timerTask()оборачивает данную лямбду в TimerTaskэкземпляр. См. Здесь: kotlinlang.org/api/latest/jvm/stdlib/kotlin.concurrent/…
Blieque,
Кроме того , данный пример может быть сжат в одну строку , если Timerобъект не будет использоваться более чем один раз, например, Timer().schedule(timerTask { ... }, 3000). Доступен также вариант, более дружественный к Kotlin; см. ответ Джонгуэра.
Blieque
11

Простой пример показа тоста через 3 секунды :

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

fun showToast(){
    Toast.makeText(context, "Its toast!", Toast.LENGTH_SHORT).show()
}
Zeero0
источник
1
могу ли я отменить звонок?
Эдуардо Оливерос,
8

Если вы ищете универсальное использование, вот мое предложение:

Создайте класс с именем Run:

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

И используйте вот так:

Run.after(1000, {
    // print something useful etc.
})
Огулкан Орхан
источник
1
Вы можете упростить это как функцию расширения
Влад
1
@Ogulcan, еще kotlinic lamda Run.after(1000) { toRun() }. Я прав
бинребин
0

Я рекомендовал использовать SingleThread, потому что вам не нужно его убивать после использования. Кроме того, в языке Котлин устарел метод stop ().

private fun mDoThisJob(){

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

    }, 1, 1, TimeUnit.SECONDS)
}

Более того, вы можете использовать его для периодической работы. Это очень полезно. Если вы хотите выполнять задание каждую секунду, вы можете установить его параметры:

Executors.newSingleThreadScheduledExecutor (). ScheduleAtFixedRate (команда Runnable, длинная начальная задержка, длинный период, единица TimeUnit);

Значения TimeUnit: наносекунды, микросекунды, миллисекунды, секунды, минуты, часы, дни.

@canerkaseler

Canerkaseler
источник
0

Если вы используете более свежие API-интерфейсы Android, пустой конструктор Handler устарел, и вам следует включить Looper. Вы можете легко пройти через это Looper.getMainLooper().

    Handler(Looper.getMainLooper()).postDelayed({
        //Your code
    }, 2000) //millis
dwbrito
источник
0

Если вы находитесь во фрагменте с областью видимости viewModel, вы можете использовать сопрограммы Kotlin:

    myViewModel.viewModelScope.launch {
        delay(2000)
        // DoSomething()
    }
Андрей
источник