I made a neverending Loop with delay(time) which i need to end by e.g. a click.
cancel() seems to be the wrong function for this.
I made a neverending Loop with delay(time) which i need to end by e.g. a click.
cancel() seems to be the wrong function for this.
What is your use case exactly? You can do this in many different ways, e.g. with some boolean flag, by cancelling, with messages sent through Channel
, etc., but it really depends. Do you run some repetitive task that you need to stop? Or maybe you need to pause some task and resume it using an external event?
Well.
As Example:
var time:long = 10000
var loop:Boolean = true
fun exampleEverlastingLoob()=GlobalScope.launch{
while(loop){
//dosmoething
delay(time)
}
}
exampleEverlastingLoob()
Now i want to change only time by e.g. click from 10000 to 3000. When i do so, i want the function to stop right away and start again whith the new time, othwise it finishes it’s jop with the old 10000 time first before it changes to 3000.
For that reason i need to kill the job first, change the time and than rerun the function right?
And that’s my question. How can i kill this? Even loop=false wont’t do it right away because the job waits for it’s 10000 to be over
For those who care
class Interval (main: MainActivity) {
var runner: Boolean = true
var time=main.time
var job:Job = GlobalScope.launch{
}
fun setInterval(function:()->(Unit))= GlobalScope.launch {
runner=true
job = launch {
while (runner) {
if (!runner) {
job. cancel()
} else {
function()
println(time)
delay(time)
}
}
}
}
fun clearInterval()= GlobalScope.launch {
runner=false
job.cancel()
}
}
Works
In that case I think killing/cancelling the loop makes sense. We just need to store Job
object returned from GlobalScope.launch()
, then invoke cancel()
on it and start again.
If for some reason you prefer not to cancel and restart, but to go with the loop continuously, you need to signal/notify the loop externally. We can do this with multiple ways, but the easiest is probably to use Channel
. Then, instead of waiting for some time to pass (delay()
), we need to wait for either time or external notification, whatever happens first. Again, we can do this in multiple ways, but built-in timeouts should do the trick.
Full example:
suspend fun main() = coroutineScope {
launch { loop() }
delay(7000)
// change interval and notify the loop
time = 1000
channel.send(Unit)
}
suspend fun loop() {
while (true) {
println("Working...")
withTimeoutOrNull(time) {
channel.receive()
}
}
}
private var time = 3000L
private val channel = Channel<Unit>()
By sending to the channel
we notify the loop to resume. If we don’t do this, it will timeout after time
.
Note that both this solution and solution based on restarts, may print “Working…” more frequently than asked. For example, we start the loop with 10s interval and then in 11th second we switch it to 5s interval. The loop will immediately resume, printing “Working…” only 1s after the last one. I don’t know if this is fine for you or not. If not then we need to somehow handle this case as well.
This would be better:
class Interval(private val time: Long) {
private var job: Job? = null
fun start(function: () -> Unit) {
job = GlobalScope.launch {
while (true) {
function()
println(time)
delay(time)
}
}
}
fun stop() {
job?.cancel()
}
}
Thanks to both of you