简体   繁体   中英

Coroutine job completing even after cancellation from external function

I have this 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:

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

Both of these functions are being run in a repository class. And it is still iterating through the whole while loop even after the above if statement is triggered. 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.

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.

I've also tried while(ensureActive) and the same thing is happening.

How do I do this?

Since this is Android, you should launch your UI-related coroutines from lifecycleScope . 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.

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. So a typical pattern inside an Activity for example might be:

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. Then you expose a function that returns the coroutine Job:

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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