我想用Kotlin协程实现计时器,类似于用RxJava实现的计时器:

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

It will emit LocalDateTime every new minute.

推荐答案

Edit:请注意,原始答案中建议的API现在标记为@ObsoleteCoroutineApi:

Ticker channels are not currently integrated with structured concurrency and their api will change in the future.

You can now use the Flow API to create your own ticker flow:

import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun tickerFlow(period: Duration, initialDelay: Duration = Duration.ZERO) = flow {
    delay(initialDelay)
    while (true) {
        emit(Unit)
        delay(period)
    }
}

你可以用一种非常类似于你当前代码的方式使用它:

tickerFlow(5.seconds)
    .map { LocalDateTime.now() }
    .distinctUntilChanged { old, new ->
        old.minute == new.minute
    }
    .onEach {
        setDateTime(it)
    }
    .launchIn(viewModelScope) // or lifecycleScope or other

注意:对于这里编写的代码,tickerFlow没有考虑处理元素所需的时间,因此延迟可能不是规则的(这是一个between元素处理的延迟).如果你想让滴答器独立于每个元素的处理而滴答作响,你可以使用buffer或专用线程(例如,通过flowOn).


原始答案

我认为这仍然是实验性的,但你可以使用TickerChannel来产生每X毫秒的值:

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

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

If you need to carry on doing your work while your "subscribe" does something for each "tick", you may launch a background coroutine that will read from this channel and do the thing you want:

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

Kotlin相关问答推荐

Kotlin 海峡没有结束

如何接受任何派生类KClass

jOOQ Kotlin Coroutines - Select all and exists查询

在 Kotlin 中定义基于多态函数的泛型函数

同时也是一个字符串的 Kotlin 枚举

区分函数和扩展

Kotlin 列表扩展功能

如何将字符串格式化为电话号码kotlin算法

我们应该在 Effect 和 Either 之间 Select 哪个作为我们业务服务的返回类型?

如何在 Android 的 Fragment 中使用 setUserVisibleHint

判断 AAR 元数据值时发现的一个或多个问题:

Kotlin 是如何编译的?

如何设置两列recyclerview?

ObserveForver是否了解生命周期?

Android 上的 Kotlin:将map到list

Kotlin:如何修改成对的值?

旋转 kotlin 数组

如何在不绑定ViewModel(MVVM)中的UI的情况下使用android导航?

Kotlin for assertThat(foo, instanceOf(Bar.class))

Kotlin 中的限制函数