在 Kotlin Multiplatform Mobile 的共享代码中实现计时器

Implement timer in shared code in Kotlin Multiplatform Mobile

我正在尝试在 Kotlin Multiplatform Mobile 项目的共享代码中实现计时器功能。计时器应 运行 持续 n 秒,并且每秒回调以更新 UI。此外,UI中的一个按钮可以取消定时器。这不可避免地意味着我必须启动某种新线程,我的问题是哪种机制适合使用 - 工人、协程或其他?

我尝试使用带有以下代码的协程,但 运行 在 iOS 上进入 InvalidMutabilityException:

class Timer(val updateInterface: (Int) -> Unit) {
    private var timer: Job? = null

    fun start(seconds: Int) {
        timer = CoroutineScope(EmptyCoroutineContext).launch {
            repeat(seconds) {
                updateInterface(it)
                delay(1000)
            }
            updateInterface(seconds)
        }
    }

    fun stop() {
        timer?.cancel()
    }
}

我确实知道 moko-time 库,但我觉得这应该可以在不依赖的情况下实现,我想了解如何操作。

正如您在评论中所怀疑的那样,updateInterface 是包含 class 的 属性,因此在 lambda 中捕获对其的引用也会冻结父级。这可能是冻结 class.

最常见和最令人困惑的方法

我会尝试这样的事情:

class Timer(val updateInterface: (Int) -> Unit) {
    private var timer: Job? = null

    init {
        ensureNeverFrozen()
    }

    fun start(seconds: Int) {
        val callback = updateInterface
        timer = CoroutineScope(EmptyCoroutineContext).launch {
            repeat(seconds) {
                callback(it)
                delay(1000)
            }
            callback(seconds)
        }
    }

    fun stop() {
        timer?.cancel()
    }
}

它有点冗长,但在 lambda 中捕获它之前为回调创建一个本地 val。

另外,添加 ensureNeverFrozen() 会给你一个堆栈跟踪,直到 class 被冻结而不是稍后在调用中。

有关详细信息,请参阅 https://www.youtube.com/watch?v=oxQ6e1VeH4M&t=1429s and a somewhat simplified blog post series: https://dev.to/touchlab/practical-kotlin-native-concurrency-ac7

我在其中一个任务中做了类似的事情,使用协程范围的扩展函数:

fun CoroutineScope.Ticker(
    tickInMillis: Long,
    onTick: () -> Unit
) {
    this.launch(Dispatchers.Default) {
        while (true) {
            withContext(Dispatchers.Main) { onTick() }
            delay(tickInMillis)
        }
    }
}

首先为两个平台实现调度程序,然后在合适的范围内调用它。