Таймер Android, который работает, когда устройство спит

Я пишу спортивное приложение, которое должно отслеживать прошедшее время квартала/половины/периода. Истекшее время должно быть точным для второго. Часы игры должны продолжать работать, даже если пользователь явно помещает устройство в спящий режим, нажав кнопку питания.

Моя первая попытка в этом заключалась в использовании Handler.postDelayed(), чтобы вызвать тактирование часов каждые 200 мс и WindowManager.LayoutParms.FLAG_KEEP_SCREEN_ON, чтобы гарантировать, что "часы" были t остановлен таймаутом экрана. Но вскоре я узнал, что можно обойти этот подход, нажав кнопку питания, чтобы вручную перевести устройство в режим сна. Кроме того, метод postDelayed() испытывает некоторый дрейф синхронизации, по-видимому, результат времени, затраченного на метод run(). Фактические цифры по-прежнему точны, но вместо выравнивания, например, на 5-секундных границах, которые легко понятны пользователям - задействованные таймеры начинают дрейфовать, что приводит к некоторой понятной путанице пользователя.

После небольшого исследования я нашел технику использования сервисов, java timers, AlarmManager и PartialWakeLock для реализации таймеров. Сервисы сами по себе не будут решать проблему, связанную с устройством, которое будет спать. Таймеры Java, такие как службы, не решают проблему с устройством, которое будет спать. AlarmManager кажется хорошим подходом, но я обеспокоен тем, что это неправильное использование AlarmManager (т.е. Очень короткие интервалы между сигналами тревоги). Использование PartialWakeLock также выглядит многообещающим, но само по себе оно не касается проблемы с дрифтом часов, с которой я столкнулся.

Я собираюсь попробовать сочетание AlarmManager и PartialWakeLock. Идея состоит в том, что AlarmManager поможет бороться с дрейфом часов и PartialWakeLock, чтобы помочь сохранить код простым (скрещенными пальцами). Я надеюсь, что такой подход приведет к разумному балансу между энергосбережением, сложностью кода и ожиданиями пользователей. Любые советы приветствуются.

Спасибо,

Рич

Ответ 1

У меня есть частичное решение для моего первоначального поста выше. Он еще не касается тактового дрейфа, связанного с временем, проведенным в вычислениях во время обработки postDelayed(), но это шаг вперед. Кроме того, он обманчиво прост, всегда хороший знак.

Оказывается, я использовал SystemClock.uptimeMillis(), когда я должен был использовать SystemClock.elapsedRealtime(). Разница между 2 является тонкой, но важной.

Как и следовало ожидать, мое решение отслеживает прошедшее время, накапливая длительность между вызовами postDelayed(), то есть прошедшее время = elapsedTime + lastClockInterval. Как указано выше, первоначальная реализация использовала uptimeMillis(). Тщательное прочтение javadoc показывает, что uptimeMillis() не включает время, затрачиваемое на "глубокий сон", например, когда пользователь нажимает кнопку питания, Но метод elapsedRealtime() включает время, проведенное в режиме "глубокого сна". Все, что требовалось для отслеживания времени в глубоких циклах сна, заключалось в замене использования uptimeMillis() с помощью elapsedRealtime(). Успех! Нет необходимости использовать AlarmManager, PartialWakeLock или что-то еще существенно более сложное. Конечно, эти методы все еще имеют применения, но они чрезмерны при реализации простых часов или таймера с истекшим временем.

Следующая проблема для решения - это дрифт часов, вызванный ненулевым временем выполнения, связанным с обработкой postDelayed(). Я надеюсь, что создание нереста для обработки приведет к решению этой проблемы, позволяя postDelayed() более или менее имитировать асинхронный вызов. Другим подходом было бы скорректировать время задержки postDelayed(), чтобы учесть время, потраченное на postDelayed(). Я опубликую свои результаты.

В отношении несвязанной заметки во время моего расследования я относился к CommonsWare Warescription. Хотя я не использовал напрямую идеи из этого источника для этой проблемы, я действительно думаю, что это будет мой Android-источник информации для обозримого будущего. У меня есть подписка O'Reilly через мою дневную работу, но я обнаружил, что книги CommonsWare являются как минимум лучшим, если не лучшим, источником информации о разработке Android в качестве ресурсов O'Reilly. И я нашел ресурсы O'Reilly Safari довольно хорошими. Интересно...

Cheers, Рич

Ответ 2

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

    Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {

    @Override
    public void run() {

        // do something here to display

        processTime();    // process what to be done on a sec by sec basis
        try {
            timerHandler.postDelayed(this, 1000
        } catch (Exception ex){

        }

    }
};

Есть ли здесь что-то, что я могу сделать, чтобы оно продолжалось в спящем режиме? Это использовать для работы на старых версиях Android/устройств.