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
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()
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With