Как уменьшить задержку Thread.sleep () в Android - PullRequest
4 голосов
/ 08 сентября 2011

Я генерирую события синхронизации в цикле в потоке (не-пользовательском интерфейсе) в приложении Android, и мне нужно, чтобы эти события происходили с точными интервалами времени (точное здесь означает, что оно не меняется больше +/- 5миллисекунды).Любые ошибки в +/- 10 миллисек (и, конечно, +/- 20 миллисек) могут быть восприняты пользователем.В верхней части этого цикла я делаю некоторые другие вычисления, которые занимают переменное количество времени, но в нижней части цикла мне нужно, чтобы событие происходило в заранее рассчитанное время.

Очень упрощенная версия(без обработки исключений) одной попытки в моем потоке, не являющемся пользовательским интерфейсом, ниже:

public final void run() {

    long loopTime = 2500L;
    long eventTime = (System.nanoTime() / 100000L) + loopTime;

    while (true) {

        calcutionsTakingVaryingAmountOfTime(); // takes 200 millisecs or less

        long eventWait = eventTime - (System.nanoTime() / 100000L);
        Thread.sleep(eventWait / 10L);
        listener.onEvent();

        eventTime = eventTime + loopTime;
    }
}

Это должен быть вызов listener.onEvent(), который должен быть точно синхронизирован.

В примеревыше, временные переменные loopTime, eventTime и eventWait измеряют время в десятых долях миллисекунды.Выражения (System.nanoTime() / 100000L), измеряющие текущее время, также выражаются в десятых долях миллисекунды.

Я абсолютно уверен, что calcutionsTakingVaryingAmountOfTime() всегда занимает менее 200 миллисекунд, и вызов listener.onEvent()это всего несколько миллисекунд.Таким образом, с loopTime, установленным на 2500L, мои события должны происходить каждые 250 миллисекунд.

Я вставил свой код (не показан), чтобы вывести на Log.d() задержку в Thread.sleep() время пробуждения.То есть я вычисляю

long latency = (System.nanoTime() / 100000L) - eventTime

сразу после возврата из Thread.sleep() и печатаю его до Log.d().

Когда я запускаю это в эмуляторе, я обнаруживаю, что latency (после деления на 10 для получения результата в миллисекундах) обычно перепрыгивает от 1 до 50 миллисекунд в последовательных проходах через цикл, сслучайные значения достигают половины секунды.При запуске на реальном устройстве дела обстоят немного лучше, но все еще немного шатко (и даже при этом поведение эмулятора заставляет задуматься, произойдет ли это на пользовательских устройствах).

Чтобы попробоватьЧтобы стабилизировать свое событие и контролировать задержку, я попробовал несколько других подходов:

  • Вместо звонка Thread.sleep(eventWait / 10L) вместо звонка на this.wait(eventWait / 10L) (совершенно неуместное ожидание (), Я знаю)

  • Я манипулировал приоритетами потоков перед входом в цикл, вызывая Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO), как это делается во всех библиотеках Android

Но с этими задержками вообще не было улучшений.

Единственный подход, который стабилизирует событие и уменьшает задержку до менее 2 или 3 миллисекунд и редко дает сбой.должен заменить вызов Thread.sleep() циклом опроса:

while ((System.nanoTime() / 100000L) < eventTime)
    ;

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

Любые идеи будут с благодарностью.

1 Ответ

2 голосов
/ 08 сентября 2011

Я использую отложенные сообщения с Handler для аналогичной цели. Это может быть немного излишним. В вашем случае я бы взглянул на Timer класс.

mTimer = new Timer();
mTimer.scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        Log.v("TEST", " tick");
    }
}, 0, 250);

Это дает мне задержку + -2 миллисекунды в эмуляторе.

...