web-dev-qa-db-fra.com

Comment implémenter une minuterie avec des coroutines Kotlin

Je veux implémenter un minuteur à l'aide de coroutines Kotlin, quelque chose de similaire à celui implémenté avec RxJava:

       Flowable.interval(0, 5, TimeUnit.SECONDS)
                    .observeOn(AndroidSchedulers.mainThread())
                    .map { LocalDateTime.now() }
                    .distinctUntilChanged { old, new ->
                        old.minute == new.minute
                    }
                    .subscribe {
                        setDateTime(it)
                    }

Il émettra LocalDateTime chaque nouvelle minute.

9
Roman Nazarevych

Je pense que c'est encore expérimental, mais vous pouvez utiliser un TickerChannel pour produire des valeurs tous les X millis:

val tickerChannel = ticker(delayMillis = 60_000, initialDelayMillis = 0)

repeat(10) {
    tickerChannel.receive()
    val currentTime = LocalDateTime.now()
    println(currentTime)
}

Si vous devez continuer à faire votre travail pendant que votre "abonnement" fait quelque chose pour chaque "tick", vous pouvez launch une coroutine en arrière-plan qui lira sur cette chaîne et fera ce que vous voulez:

val tickerChannel = ticker(delayMillis = 60_000, initialDelayMillis = 0)

launch {
    for (event in tickerChannel) {
        // the 'event' variable is of type Unit, so we don't really care about it
        val currentTime = LocalDateTime.now()
        println(currentTime)
    }
}

delay(1000)

// when you're done with the ticker and don't want more events
tickerChannel.cancel()

Si vous voulez vous arrêter de l'intérieur de la boucle, vous pouvez simplement en sortir, puis annuler le canal:

val ticker = ticker(500, 0)

var count = 0

for (event in ticker) {
    count++
    if (count == 4) {
        break
    } else {
        println(count)
    }
}

ticker.cancel()
18
Joffrey

Edit: Joffrey a édité sa solution avec une meilleure approche.

Vieux :

Joffrey la solution fonctionne pour moi mais j'ai rencontré un problème avec la boucle for.

Je dois annuler mon ticker dans la boucle for comme ceci:

            val ticker = ticker(500, 0)
            for (event in ticker) {
                if (...) {
                    ticker.cancel()
                } else {
                    ...
                    }
                }
            }

Mais ticker.cancel() lançait une exceptionException car la boucle for continuait après cela.

J'ai dû utiliser une boucle while pour vérifier si le canal n'était pas fermé pour ne pas obtenir cette exception.

                val ticker = ticker(500, 0)
                while (!ticker.isClosedForReceive && ticker.iterator().hasNext()) {
                    if (...) {
                        ticker.cancel()
                    } else {
                        ...
                        }
                    }
                }
1
Benjamin Ledet

Vous pouvez créer un compte à rebours comme celui-ci

GlobalScope.launch(Dispatchers.Main) {
            val totalSeconds = TimeUnit.MINUTES.toSeconds(2)
            val tickSeconds = 1
            for (second in totalSeconds downTo tickSeconds) {
                val time = String.format("%02d:%02d",
                    TimeUnit.SECONDS.toMinutes(second),
                    second - TimeUnit.MINUTES.toSeconds(TimeUnit.SECONDS.toMinutes(second))
                )
                timerTextView?.text = time
                delay(1000)
            }
            timerTextView?.text = "Done!"
        }
0
murgupluoglu