Повторите задачу в течение срока с задержкой - PullRequest
2 голосов
/ 10 апреля 2019

Мне нужно переместить индикатор в течение определенного периода времени, например, в течение 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.

По сути, я просто хочу повторитьувеличьте индикатор выполнения за определенный промежуток времени, и я не использую анимацию из-за проблем с производительностью.

Что-то мне не хватает?

Ответы [ 3 ]

0 голосов
/ 10 апреля 2019

так что вы делаете здесь, имитируя прогресс.В идеале, должен быть какой-то способ проверки фактического прогресса вашего бара, его обновления и, когда это будет сделано, окончания.Но если это невозможно, то да, симуляция - ваш выбор.

Итак, с сопрограммами мы имеем дело с многопоточным окружением, и в этом у нас есть наши сопрограммы, которые нужно продолжать, когда руканад контролем исполнения.В вашей реализации это происходит при вызове 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()
} 
0 голосов
/ 10 апреля 2019

Вы измеряете не только задержку в 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")
}
0 голосов
/ 10 апреля 2019

сопрограмма не обеспечивает точного времени. Если процессор занят выполнением других программ одновременно, сопрограммы могут быть легко задержаны. Используйте Класс таймера для точной синхронизации. Вот пример. Я регистрирую время каждую секунду и отменяю таймер через 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)
...