web-dev-qa-db-ja.com

Kotlinコルーチンでタイマーを実装する方法

Kotlinコルーチンを使用してタイマーを実装したいのですが、これはRxJavaで実装されたこれに似ています。

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

LocalDateTimeは新しい分ごとに発行されます。

9

私はそれがまだ実験的であると信じていますが、 TickerChannel を使用してXミリ秒ごとに値を生成することができます:

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

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

「サブスクライブ」が「ティック」ごとに何かを実行している間に作業を続ける必要がある場合は、このチャネルから読み取り、必要なことを行うバックグラウンドコルーチンをlaunch実行できます。

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()

ループの内側から停止したい場合は、ループから抜け出し、チャネルをキャンセルするだけです。

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 は、より良いアプローチで彼のソリューションを編集しました。

古い:

Joffrey の解決策は私には有効ですが、forループで問題が発生しました。

次のようにforループでティッカーをキャンセルする必要があります。

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

しかし、ticker.cancel()はcancelExceptionをスローしていました。これは、forループがこの後に続いていたためです。

この例外が発生しないようにするには、whileループを使用してチャネルが閉じられていないかどうかを確認する必要がありました。

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

このようなカウントダウンタイマーを作成できます

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