【问题标题】:Best way to stop and repeat coroutine, kotlin停止和重复协程的最佳方式,kotlin
【发布时间】:2021-11-22 15:44:20
【问题描述】:

我有一个 android 应用程序,可以在滑块移动时绘制耗时的图表。如果滑块移动得很快,我想停止当前的图形绘制并重新启动它。一旦滑块停止,我就完成了最后一项工作。 我正在使用协程来绘制它。停止协程然后重新启动它的最佳方法是什么?

【问题讨论】:

标签: android kotlin coroutine restart cancellation


【解决方案1】:

协程没有内置的重启操作,但您可以只cancel() 已经在运行一个,然后再次调用launch()(或任何其他协程构建器)以从头开始。

但是...对于您的具体情况,我建议阅读reactive programming,尤其是:RxJavaKotlin flows。它们是专门为解决这类问题而发明的。

查看这个使用流的示例:

suspend fun main() {
    // simulate slider position changes
    val sliderFlow = flow {
        emit(1)
        delay(50)
        emit(2)
        delay(50)
        emit(3)
        delay(50)
        emit(4)
        delay(800)
        emit(5)
        delay(1500)
        emit(6)
    }
    sliderFlow.collectLatest { drawGraph(it) }
}

suspend fun drawGraph(input: Int) {
    println("started drawing $input...")
    try {
        delay(1000) // simulate drawing
    } catch (e: CancellationException) {
        println("cancelled drawing $input")
        throw e
    }
    println("ended drawing $input")
}

sliderFlow 是滑块位置流(为测试目的而创建)。对于每个新位置 drawGraph() 都会被调用,这需要 1000 毫秒才能完成。由于我们只对最新位置感兴趣,因此我们使用collectLatest(),如果有新值到达,它会自动取消对值的处理。它产生这个输出:

started drawing 1...
cancelled drawing 1
started drawing 2...
cancelled drawing 2
started drawing 3...
cancelled drawing 3
started drawing 4...
cancelled drawing 4
started drawing 5...
ended drawing 5
started drawing 6...
ended drawing 6

我们可以看到,只有 56 完成了,其余的被取消了,因为在 drawGraph() 能够完成之前发出了新的滑块值。

此外,如果滑块可以移动得非常快,那么我们可能不想为每个新值开始绘制,因为那样我们将开始绘制只是在几毫秒后取消。在这种情况下,我们可以使用debounce(),它会在指定的时间内等待值“稳定”:

sliderFlow
    .debounce(100)
    .collectLatest { drawGraph(it) }

输出:

started drawing 4...
cancelled drawing 4
started drawing 5...
ended drawing 5
started drawing 6...
ended drawing 6

如我们所见,我们甚至没有开始绘制 123,因为它们被新值替换得太快了。

【讨论】:

  • delay 是一个带有相互抵消功能的挂起函数 - 如果您在收集器中进行阻塞计算工作,即大量绘图计算而不是模拟绘图,会发生什么?由于取消必须是相互的(您可能使用 Thread.sleep(1000) 而不是 delay(1000) 来模拟),因此排放不会是顺序的吗?此外,您必须在绘图函数中进行定期检查,即 isActive 并返回以停止计算或抛出显式 CancellationException 以释放下一个上游发射
  • 是的,绘图操作必须配合取消,例如通过不时检查isActive。我没有提到这一点,因为 OP 明确询问了取消和重新启动协程的调度,而不是关于如何实现绘图以便响应取消。我的印象是绘图已经可以取消,或者这不是问题。
  • 我想理想的情况是,如果您正在绘制一个图形,其中有一些点循环执行大部分绘图功能工作,这些点很容易被挂钩以检查取消。很高兴我们在相互取消和上游排放的同一页面上,因为这是一个真正的健全性检查问题!
  • 非常感谢。仅适用于取消。只需要检查是否 isActive 在某些地方。以后也会尝试使用flow,看起来是处理这类事情的好工具
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-27
  • 2012-01-20
  • 1970-01-01
  • 2019-11-15
  • 2010-10-20
  • 2019-05-13
  • 2021-07-27
相关资源
最近更新 更多