Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Stop Or Cancel A Kotlin Coroutine (Stop Currently Running Code Block Instantly)?

What I want to achieve?

I have a task to download images but as the screen scrolls it will cancel previous downloads and start downloading new ones. I want that when it cancel the coroutine downloading previous image it stop instantly and free up the bandwidth so new images download faster.

What I have tried?

I have tried multiple ways to stop the coroutine but it keeps going until it finishes the downloading even after cancelling the coroutine. When I cancel the coroutine it makes a variable isActive to false and stop calling further suspended function. But the problem is if its running a loop for 1000000 times or downloading an image from network this task will not canceled unless completed. Like loop will complete it's 1000000 iterations then the coroutine will be cancelled.

I have tried these but no success:

job.cancel()
scope.cancel()

I have tried so many ways for achieving this but got no solution. I can't use any library right now in my project.

This use case not achieved by Threads, Executor Service, Coroutines. Because all behave the same.

More questions same like this :

How do I cancel a kotlin coroutine for a blocking download operation

AsyncTask is not cancelling the long running operation in android

Leaking Service held by Coroutine

like image 477
Abdur Rahman Avatar asked Sep 13 '25 13:09

Abdur Rahman


2 Answers

A kotlin coroutine must cooperate to allow cancellation. That means it has some check points calling one suspend function. This makes sense as some procedures are atomic and should not be stopped in the middle.

One example of bad coroutine that can not be cancelled:

    var job = launch {
        var time = System.currentTimeMillis()
        var i = 0
        while (i < 1000) {
            if (System.currentTimeMillis() >= time) {
                println("Loop number ${++i} ")
                time += 500
            }
        }
    }

To make it cancellable, you can add yield() at the begining of each iteration. Following is a cancellable coroutine:

coroutineScope {
    var job = launch {
        var time = System.currentTimeMillis()
        var i = 0
        while (i<1000) {
            yield()
            if (System.currentTimeMillis() >= time) {
                println("Loop number ${++i}")
                time += 500
            }
        }
    }
    // wait some time
    delay(1300)
    println("Stopping the coroutine....")
    job.cancel()
    job.join()
    // or call job.cancelAndJoin()
}
like image 187
AIMIN PAN Avatar answered Sep 15 '25 02:09

AIMIN PAN


Coroutine cancellation is cooperative. A coroutine code has to cooperate to be cancellable. All the suspending functions in kotlinx.coroutines are cancellable. They check for cancellation of coroutine and throw CancellationException when cancelled. However, if a coroutine is working in a computation and does not check for cancellation, then it cannot be cancelled, like the following example shows:

val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
    var nextPrintTime = startTime
    var i = 0
    while (i < 5) { // computation loop, just wastes CPU
        // print a message twice a second
        if (System.currentTimeMillis() >= nextPrintTime) {
            println("job: I'm sleeping ${i++} ...")
            nextPrintTime += 500L
        }
    }
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")

Run it to see that it continues to print "I'm sleeping" even after cancellation until the job completes by itself after five iterations

Making computation code cancellable

like the following example shows:

val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
    var nextPrintTime = startTime
    var i = 0
    while (isActive) { // cancellable computation loop
        // print a message twice a second
        if (System.currentTimeMillis() >= nextPrintTime) {
            println("job: I'm sleeping ${i++} ...")
            nextPrintTime += 500L
        }
    }
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")

Refer to the official docs here

like image 20
End User Avatar answered Sep 15 '25 04:09

End User