简体   繁体   English

即使从外部 function 取消后,协程作业也会完成

[英]Coroutine job completing even after cancellation from external function

I have this function:我有这个 function:

suspend fun functionCall(): Job {
      return MainScope().launch {
       var i = 0
       while(i < 3) {
         i++
         delay(3000)
         yield()
      }
     }
    cancel()
}

And I am calling from an external function when a button is clicked:当单击按钮时,我从外部 function 调用:

MainScope().launch {
   if(functionCall().isActive) {
      functionCall().cancelAndJoin()
   }
}

Both of these functions are being run in a repository class.这两个函数都在存储库 class 中运行。 And it is still iterating through the whole while loop even after the above if statement is triggered.即使在上面的 if 语句被触发之后,它仍然会遍历整个 while 循环。 What I'm noticing while debugging is "i" is also being reset to 0 which could indicate the job is being triggered more than once but it is definitely being triggered only once so I'm confused about what is happening.我在调试时注意到“i”也被重置为 0,这可能表明该作业被多次触发,但它肯定只被触发一次,所以我对正在发生的事情感到困惑。

What I want to happen is after that if statement for the entire job to cancel and for the entire function to return and run no more code.我想要发生的是在那之后取消整个作业的 if 语句以及整个 function 返回并不再运行代码。

I've also tried while(ensureActive) and the same thing is happening.我也尝试过 while(ensureActive) 并且发生了同样的事情。

How do I do this?我该怎么做呢?

Since this is Android, you should launch your UI-related coroutines from lifecycleScope .由于这是 Android,因此您应该从lifecycleScope范围启动与 UI 相关的协程。 If you have a job that needs to survive screen rotations, you should launch it from inside a ViewModel from viewModelScope , and it must not touch any UI elements.如果您的作业需要在屏幕旋转中幸存下来,您应该从viewModelScope的 ViewModel 内部启动它,并且它不能触及任何 UI 元素。

If you want to cancel a specific coroutine when an event happens, you should store that coroutine Job in a property so you can call cancel() on it.如果您想在事件发生时取消特定的协程,您应该将该协程 Job 存储在一个属性中,以便您可以在其上调用cancel() So a typical pattern inside an Activity for example might be:因此,例如 Activity 中的典型模式可能是:

private var fooJob: Job? = null

private fun fooSomething() {
    fooJob = lifecycleScope.launch {
        repeat(5) {
            delay(1000)
            Log.i("count", it.toString())
        }
    }
}

private fun cancelCurrentFoo() {
    fooJob?.cancel()
}

Suppose you have a coroutine job you can start by calling one of the functions of your ViewModel, but you want the Activity/Fragment to be able to cancel it early.假设您有一个协程作业,您可以通过调用 ViewModel 的一个函数开始,但您希望 Activity/Fragment 能够提前取消它。 Then you expose a function that returns the coroutine Job:然后公开一个返回协程 Job 的 function:

fun foo() = viewModelScope.launch {
    repeat(5) {
        delay(1000)
        Log.i("count", it.toString())
    }
}

The Activity can call this function and it gets a Job instance in return that it can call cancel() on whenever it wants. Activity 可以调用此 function 并获得一个 Job 实例作为回报,它可以随时调用cancel()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM