Я должен переместить индикатор в течение определенного периода времени, например, в течение 6 секунд. Я использую сопрограммы и функцию "повторить". Код выполняется за исключением того, что общее время выполнения не соответствует указанному. Ниже мой код.
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}")
}
Я ожидаю, что "общее время" будет 6000, но я получаю значения, превышающие 6000, по крайней мере, на 500.
По сути, я просто хочу многократно увеличивать индикатор выполнения в течение определенного периода времени, и я не использую анимацию из-за проблем с производительностью.
Есть ли что-то, что я пропускаю?
Вы измеряете не только задержку в 6 миллисекунд, но и время, необходимое для выполнения цикла for
(скрытое в repeat
), плюс время progressBar.progress += 1
и стоимость самой delay
.
Например:
CoroutineScope(Dispatchers.Default + progressJob).launch {
startTime = System.currentTimeMillis()
repeat(1000){
delay(6)
}
val endTime = System.currentTimeMillis() - startTime
println("total time= $endTime")
}
на моей машине требуется 6751 мс (в среднем 100 пробежек).
Если я использую Thread.sleep
вместо задержки:
CoroutineScope(Dispatchers.Default + progressJob).launch {
startTime = System.currentTimeMillis()
repeat(1){
delay(6)
}
val endTime = System.currentTimeMillis() - startTime
println("total time= $endTime")
}
это занимает 6701мс.
Если я выполню повторить только один раз:
CoroutineScope(Dispatchers.Default + progressJob).launch {
startTime = System.currentTimeMillis()
repeat(1){
Thread.sleep(6)
}
val endTime = System.currentTimeMillis() - startTime
println("total time= $endTime")
}
8ms
Если я удалю повторить:
CoroutineScope(Dispatchers.Default + progressJob).launch {
startTime = System.currentTimeMillis()
Thread.sleep(6)
val endTime = System.currentTimeMillis() - startTime
println("total time= $endTime")
}
Сопрограмма не дает точных сроков. Если процессор занят выполнением других программ одновременно, сопрограммы могут быть легко задержаны. Используйте класс таймера для точного времени. Вот пример. Я регистрирую время каждую секунду и отменяю таймер через 6 секунд. Результаты отключаются только на несколько миллисекунд.
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)
так что вы делаете здесь, имитируя прогресс. В идеале, должен быть какой-то способ проверки фактического прогресса вашего бара, его обновления и, когда это будет сделано, окончания. Но если это невозможно, тогда симуляция - ваш выбор.
Итак, с сопрограммами мы имеем дело с многопоточным окружением, и в рамках этого у нас есть наши сопрограммы, которые необходимо продолжать, когда передается контроль над исполнением. В вашей реализации это происходит при delay
вызова. По этой причине очень трудно гарантировать, что ваша сопрограмма будет завершена в желаемое время. Все, что может сделать задержка, это сказать, что она не возобновится до того, как "по крайней мере" истечет указанное время, и, вероятно, довольно часто пройдет больше времени, а не точное время.
Итак, как нам заставить это исполниться как можно ближе к желаемому времени? Что нам нужно сделать, это отбросить repeat
, и, скорее, проверить, сколько времени прошло, чтобы решить, закончим ли мы. Вот примерная реализация, которая, надеюсь, поможет.
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()
}
repeat
и замена на while
- это ключ. В среднем общее время составляет 601X
, что достаточно близко для моего случая использования. Большое спасибо.
delay
внутри цикла, пока вы не укажете на это. Я думаю, это то, откуда приходит дополнительное время