我正在探索在Android UI线程上下文中使用协同例程。我按照Coroutines Guide UI中的描述实施了contextJob
。后台工作从GUI开始,我想每次点击都重新启动它(停止正在运行的程序并重新启动它)。有没有办法重用Job实例?
但一旦取消,不能重用所以即使创建一个孩子作业的作业:
val job = Job(contextJob)
,并取消它并没有帮助,因为它必须-重新分配。
有没有办法重用Job实例?
我正在探索在Android UI线程上下文中使用协同例程。我按照Coroutines Guide UI中的描述实施了contextJob
。后台工作从GUI开始,我想每次点击都重新启动它(停止正在运行的程序并重新启动它)。有没有办法重用Job实例?
但一旦取消,不能重用所以即使创建一个孩子作业的作业:
val job = Job(contextJob)
,并取消它并没有帮助,因为它必须-重新分配。
有没有办法重用Job实例?
A Job有一个非常简单的设计生命周期。其“完成”状态是最后的,非常类似于Android Activity
的“销毁”状态。因此,如指南中所述,父母Job
最好与Activity
关联。只有当活动被破坏时,您才应取消父作业。由于被摧毁的活动无法重复使用,因此您将永远不会需要重用其作业。
对每次点击开始工作的推荐方法是使用参与者,因为他们可以帮助您避免不必要的并发。该指南介绍了如何在每次点击时启动它们,但不显示如何取消当前正在运行的操作。
您需要的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
}
}
一个演员始终处于激活状态,直到它的父作业(连接到活动) 取消。演员等待点击并在每次点击时启动action
。但是,action
的每个调用都使用withContext
块封装到其自己的Job
中,以便可以从其父作业中单独取消它。
请注意,此代码适用于不可取消的操作或只需要一些时间取消的操作。当取消操作时,可能需要清理其资源,并且由于此代码使用了一个参与者,因此可以确保在开始下一个操作之前完成上一个操作的清理。