11

我正在探索在 Android UI 线程的上下文中使用协程。我按照Coroutines Guide UIcontextJob中的描述实现了。后台工作是从 GUI 开始的,我想在每次点击时重新启动它(停止当前正在运行的并重新启动它)。

但是一旦取消的工作就不能被重用,所以即使创建一个子工作:

 val job = Job(contextJob)

并且取消它没有帮助,因为它必须重新分配。

有没有办法重用 Job 实例?

4

1 回答 1

10

Job的生命周期设计非常简单。它的“Completed”状态是final,非常类似于Android的“Destroyed”状态Activity。因此,父母Job最好与 相关联Activity,如指南中所述。当且仅当活动被破坏时,您应该取消父作业。因为一个被破坏的活动不能被重用,你永远不会遇到重用它的工作的需要。

每次点击开始工作的推荐方法是使用参与者,因为它们可以帮助您避免不必要的并发。该指南显示了如何在每次单击时启动它们,但没有显示如何取消当前正在运行的操作。

您将需要一个新的实例,JobwithContext使代码块可以与其他所有内容分开取消:

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在每次点击时启动。但是,an 的每次调用都action被包装到它自己的JobusingwithContext块中,因此它可以与其父作业分开取消。

请注意,此代码适用于不可取消的操作或仅需要一些时间才能取消的操作。当一个动作被取消时,它可能需要清理它的资源,并且由于这段代码使用了一个actor,它确保在下一个动作开始之前完成前一个动作的清理。

于 2017-03-16T10:25:04.253 回答