Repeat a task within a duration with delay
Solution 1
so what you are doing here is simulating progress. Ideally, there would be some way of checking the actual progress of your bar, and updating it, and when it is done, ending. But, if this is not possible, then ya, simulation is your choice.
So, with coroutines we are dealing with a threaded environment, and within that, we have our coroutines which need to be continued when the hand over control of execution. In your implementation, this happens at the delay
call. For this reason, it is very difficult to guarantee that your coroutine will complete in your desired time. All delay can do is say that it will not resume before "at least" the specified time has elapsed, and probably quite often, more time would have elapsed, not the exact time.
So, how do we get this to execute in as close to your desired time frame as possible? What we need to do is drop the repeat
, and rather check on the elapsed time to decide if we finish. Here is a rough implementation that will hopefully help.
class Bar(val barLength: Int = 1000) {
var progress = 0
}
suspend fun simulateProgress(bar: Bar, job: Job, totalDurationMillis: Long, incrementsMills: Long): Job {
var startTime = System.currentTimeMillis()
return CoroutineScope(Dispatchers.Default + job).launch {
var totalElapsed = 0L
while (totalElapsed < totalDurationMillis) {
totalElapsed = System.currentTimeMillis() - startTime
val progressRatio = totalElapsed.toDouble()/totalDurationMillis.toDouble()
bar.progress = (progressRatio * bar.barLength.toDouble()).toInt()
delay(incrementsMills)
}
println("Elapsed: $totalElapsed, Progress: ${bar.progress}")
}
}
fun main() = runBlocking {
val job = Job()
val bar = Bar()
val progressJob = simulateProgress(bar, job, 6000, 10)
progressJob.join()
}
Solution 2
I would do it with something like this:
withTimeout(1300L) {
repeat(1000) { i ->
println("Blip Blop $i ...")
delay(500L)
}
}
For more examples see official doc: https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html
Solution 3
Coroutine does not provide precise timing. If the processor is busy running other stuff at the same time, coroutines can be easily delayed. Use the Timer Class for precise timing. Here is an example. I log the time every seconds and cancel the timer after 6 seconds. The results are only off by a few milliseconds.
var startTime = 0L
val timer : Timer = Timer()
val task = object : TimerTask()
{
var lastTime = 0L
override fun run() {
val now = System.currentTimeMillis()
if(now/1000 > lastTime/1000 )
{
Log.d("timer","total time= ${now - startTime}")
lastTime = now
}
if(now - startTime >= 6000)
{
timer.cancel()
}
}
startTime = System.currentTimeMillis()
timer.scheduleAtFixedRate(task,0,6)
Hayton Leung
Updated on June 18, 2022Comments
-
Hayton Leung almost 2 years
I have to move a progress bar within a time frame, for example within 6s. I am using coroutines and the "repeat" function. The code executes except that the total execution time is not as specified. Below is my code.
val progressJob = Job() var startTime = 0L CoroutineScope(Dispatchers.Default + progressJob).launch { startTime = System.currentTimeMillis() repeat(1000) { progressBar.progress += 1 delay(6) } Log.d(TAG, "total time= ${System.currentTimeMillis() - startTime}") }
I am expecting "total time" would be 6000, but I am getting values greater than 6000 by at least 500.
Basically I just want to repeatedly increment the progress bar within a time frame, and I am not using animation because of performance issue.
Is there anything I am missing?
-
Hayton Leung about 5 yearsThank you for your explanation. I didn't notice the for loop inside
delay
until you point it out. I guess this is where the extra time comes from -
Hayton Leung about 5 yearsFor some other requirements I will have to stick with coroutines, but thanks for your suggestion.
-
Hayton Leung about 5 yearsDropping the
repeat
and replace withwhile
is the key. On average the total time is601X
, which is close enough for my use case. Thanks a lot. -
Laurence about 5 yearsJust for info - you should not use Thread tools when you are achieving concurrency with coroutines. It sort of defeats the whole point.
-
Laurence about 5 years@HaytonLeung - no problem.