有没有办法重用 Job 实例?
Is there a way to reuse a Job instance?
我正在探索在 Android UI 线程的上下文中使用协程。我按照 Coroutines Guide UI 中的描述实现了 contextJob
。后台工作从 GUI 开始,我想在每次点击时重新启动它(停止当前 运行 并重新启动)。
但是作业一旦被取消就不能再使用,所以即使创建子作业也是如此:
val job = Job(contextJob)
取消它也无济于事,因为它必须重新分配。
有没有办法重用作业实例?
A Job 在设计上有一个非常简单的生命周期。它的 "Completed" 状态是 final,非常类似于 Android Activity
的 "Destroyed" 状态。因此,如指南中所述,父 Job
最好与 Activity
相关联。当且仅当 activity 被销毁时,您才应该取消父作业。因为销毁的 activity 无法重复使用,所以您永远不会 运行 需要重复使用它的工作。
在每次点击时开始工作的推荐方法是使用参与者,因为它们可以帮助您避免不必要的并发。该指南显示了如何在每次单击时启动它们,但未显示如何取消当前 运行ning 操作。
您将需要 Job
的新实例与 withContext
的组合,以使代码块可与其他所有内容分开取消:
fun View.onClick(action: suspend () -> Unit) {
var currentJob: Job? = null // to keep a reference to the currently running job
// launch one actor as a parent of the context job
// actor prevent concurrent execution of multiple actions
val eventActor = actor<Unit>(contextJob + UI, capacity = Channel.CONFLATED) {
for (event in channel) {
currentJob = Job(contextJob) // create a new job for this action
try {
// run an action within its own job
withContext(currentJob!!) { action() }
} catch (e: CancellationException) {
// we expect it to be cancelled and just need to continue
}
}
}
// install a listener to send message to this actor
setOnClickListener {
currentJob?.cancel() // cancel whatever job we were doing now (if any)
eventActor.offer(Unit) // signal to start next action when possible
}
}
在其父作业(附加到 activity)被取消之前,actor 始终处于活动状态。演员等待点击并在每次点击时启动 action
。但是,action
的每个调用都使用 withContext
块包装到它自己的 Job
中,因此它可以与其父作业分开取消。
请注意,此代码适用于不可取消或需要一些时间才能取消的操作。一个动作在被取消时可能需要清理它的资源,并且因为这段代码使用了一个参与者,所以它确保在下一个动作开始之前完成前一个动作的清理。
我正在探索在 Android UI 线程的上下文中使用协程。我按照 Coroutines Guide UI 中的描述实现了 contextJob
。后台工作从 GUI 开始,我想在每次点击时重新启动它(停止当前 运行 并重新启动)。
但是作业一旦被取消就不能再使用,所以即使创建子作业也是如此:
val job = Job(contextJob)
取消它也无济于事,因为它必须重新分配。
有没有办法重用作业实例?
A Job 在设计上有一个非常简单的生命周期。它的 "Completed" 状态是 final,非常类似于 Android Activity
的 "Destroyed" 状态。因此,如指南中所述,父 Job
最好与 Activity
相关联。当且仅当 activity 被销毁时,您才应该取消父作业。因为销毁的 activity 无法重复使用,所以您永远不会 运行 需要重复使用它的工作。
在每次点击时开始工作的推荐方法是使用参与者,因为它们可以帮助您避免不必要的并发。该指南显示了如何在每次单击时启动它们,但未显示如何取消当前 运行ning 操作。
您将需要 Job
的新实例与 withContext
的组合,以使代码块可与其他所有内容分开取消:
fun View.onClick(action: suspend () -> Unit) {
var currentJob: Job? = null // to keep a reference to the currently running job
// launch one actor as a parent of the context job
// actor prevent concurrent execution of multiple actions
val eventActor = actor<Unit>(contextJob + UI, capacity = Channel.CONFLATED) {
for (event in channel) {
currentJob = Job(contextJob) // create a new job for this action
try {
// run an action within its own job
withContext(currentJob!!) { action() }
} catch (e: CancellationException) {
// we expect it to be cancelled and just need to continue
}
}
}
// install a listener to send message to this actor
setOnClickListener {
currentJob?.cancel() // cancel whatever job we were doing now (if any)
eventActor.offer(Unit) // signal to start next action when possible
}
}
在其父作业(附加到 activity)被取消之前,actor 始终处于活动状态。演员等待点击并在每次点击时启动 action
。但是,action
的每个调用都使用 withContext
块包装到它自己的 Job
中,因此它可以与其父作业分开取消。
请注意,此代码适用于不可取消或需要一些时间才能取消的操作。一个动作在被取消时可能需要清理它的资源,并且因为这段代码使用了一个参与者,所以它确保在下一个动作开始之前完成前一个动作的清理。