在 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)
}
}
}
首先为两个平台实现调度程序,然后在合适的范围内调用它。
我正在尝试在 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)
}
}
}
首先为两个平台实现调度程序,然后在合适的范围内调用它。