有没有办法重用Job实例?

ato*_*tok 8 android coroutine async-await kotlin kotlin-coroutines

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

但是一旦被取消的工作就无法重复使用,所以甚至创造了一份儿童工作:

 val job = Job(contextJob)
Run Code Online (Sandbox Code Playgroud)

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

有没有办法重用Job实例?

Rom*_*rov 6

一个工作有设计一个非常简单的生命周期.它的"已完成"状态是最终状态,非常类似于Android的"已毁"状态Activity.因此,如指南中所述,父母Job最好与a 关联Activity.当且仅当活动被销毁时,您应该取消父作业.由于被破坏的活动无法重复使用,因此您永远不会遇到重用其工作的需要.

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

您将需要一个新的实例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
    }
}
Run Code Online (Sandbox Code Playgroud)

在其父作业(附加到活动)被取消之前,actor始终处于活动状态.演员等待点击并action在每次点击时开始.但是,每次调用a都会action被包装到自己的Jobusing withContext块中,以便可以与其父作业分开取消.

请注意,此代码适用于不可取消或只需要一些时间取消的操作.操作可能需要在取消时清理其资源,并且,因为此代码使用了actor,所以它确保在下一个操作开始之前完成上一个操作的清理.