setAlarmClock() срабатывает слишком поздно в спящем режиме

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

AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);        
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent penInt = PendingIntent.getBroadcast(this, intentId, intent, 0);

Этот метод различия между уровнями API я нашел здесь, в stackoverflow, и поместил его в свою функцию calcNextAlarm() (плюс несколько сообщений журнала для отладки), чтобы правильно установить сигнал тревоги независимо от того, какой API используется на устройстве:

// problems in doze mode api 23+
if (Build.VERSION.SDK_INT >= 23) {
    if (testMode) Log.d("Ben", "setAlarmClock() - API 23+");
    am.setAlarmClock(new AlarmManager.AlarmClockInfo(alarmTimeInMillis, penInt), penInt);
}

else if (Build.VERSION.SDK_INT >= 19) {
// Wakes up the device in Idle Mode
    if (testMode) Log.d("Ben", "setExact() - API >= 19 && API < 23");
    am.setExact(AlarmManager.RTC_WAKEUP, alarmTimeInMillis, penInt);
}
// Old APIs
else {
    if (testMode) Log.d("Ben", "set() - API < 19");
    am.set(AlarmManager.RTC_WAKEUP, alarmTimeInMillis, penInt);
}

Согласно сообщениям Log.d, я вижу, что на моем устройстве Android 7.1 выполняется первый метод setAlarmClock() для установки будильника в Receiver.

Я действительно впадаю в отчаяние после 3 недель безуспешных тестов и кодирования - мой будильник сегодня снова сработал на 4 минуты позже - этого никогда не должно произойти, согласно странице обучения режиму дремоты:

Будильники, установленные с помощью setAlarmClock(), продолжают нормально срабатывать — система выходит из режима ожидания незадолго до срабатывания этих будильников.

На моем телефоне 7.1 будильник даже запаздывает от 20 секунд до 1:40 минут, когда я устанавливаю будильник на «сейчас +5 или 6» минут. Может ли кто-нибудь посоветовать мне, как действительно ВСЕГДА отключить сигнализацию вовремя?


person Ben    schedule 28.12.2017    source источник


Ответы (2)


Попробуйте использовать:

setExactAndAllowWhileIdle()

Это обеспечит своевременное срабатывание сигнализации. Я проверил это в своем собственном приложении, и оно надежно.

Если вы ориентируетесь на API меньше 23, сохраните это в предложении if, которое проверяет текущий API, установленный на устройстве. Используйте это только выше уровня API 23, для остальных продолжайте использовать setExact

Хорошо, чтобы ответить на ваш комментарий:

1) я точно проверил его в спящем режиме без подключенного аккумулятора

2) да, к сожалению, есть ограничение по одному будильнику в 9 минут для будильников в режиме ожидания. Здесь у вас есть две альтернативы:

Сначала используйте setExactAndAllowWhileIdle для срабатывания первого будильника.

а) Для повтора вам придется использовать метод setAlarmClock

b) для отсрочки вы можете запланировать задание JobScheduler с минимальным временем задержки, установленным в качестве времени отсрочки. Это гарантирует, что задание будет запланировано, по крайней мере, в промежутке интервала повтора. Но только это приведет к тому, что задание будет запланировано случайным образом после интервала, поэтому вам также необходимо установить крайний срок переопределения равным 0, чтобы задание было запланировано сразу после минимальной задержки. Также оставьте сетевые требования равными «Нет», а требуемый режим ожидания — по умолчанию или «ложным». По моему опыту, это так же надежно, как и точные методы сигнализации, и я лично использую этот подход.

person Kushan    schedule 28.12.2017
comment
Это также не покажет потенциальное уведомление пользователю, например метод setAlarmClock. Теперь это не совсем точно для t, но, согласно моему опыту, относительно точно составляет около 5-10 секунд. Последней отчаянной альтернативой было бы иметь сообщение FCM с высоким приоритетом, попадающее на устройство в нужное вам время, но это будет зависеть от сетевого подключения. - person Kushan; 28.12.2017
comment
Я уверен, что пробовал это раньше, но я в таком отчаянии, что попробую :) 1: Вы уверены, что это точно на батарее и не заряжается? (Поскольку все методы кажутся совершенно точными при подключении к кабелю питания, а не в режиме ожидания) 2: Согласно странице обучения дремоте: Note: Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 9 minutes, per app. Если я хочу вздремнуть в течение 5 минут - должен ли я использовать обычный setAlarmClock() тогда для повтора вместо setExactAndAllowWhileIdle()? 3: целевой API 27 и минимальный SDK 16. - person Ben; 28.12.2017
comment
Да, попробуйте, два, которые я указал, и тот, который вы уже используете, - единственные, которые в любом случае имеют дело с режимом дремоты. Разрешить во время простоя работает лучше всего для меня. Проверял на нескольких устройствах. У меня всегда хорошо работает - person Kushan; 28.12.2017
comment
Извините, я не знал, что комментарий будет отправлен при нажатии ввода :) Я немного отредактировал его, если вы не заметили: не могли бы вы просмотреть мои вопросы, пожалуйста? - person Ben; 28.12.2017
comment
Проверьте мой ответ, теперь есть две альтернативы для вашего затруднительного положения. С минимальным SDK 16 я не уверен в планировщике заданий, поэтому, думаю, вы можете использовать вариант а). В противном случае планировщик заданий лучше всего подходит для новейших устройств, поскольку Android имеет тенденцию обманывать людей, которые не используют его в настоящее время. - person Kushan; 28.12.2017
comment
Хорошо, теперь я реализовал ...allowWhileIdle для основных будильников и setAlarmClock для 5-минутных таймеров повтора. Остальное я оставил как есть внутри IF. Думаю, мне придется протестировать его через несколько дней, пока спасибо - я проголосую за ваш ответ / приму ваш ответ, как только смогу подтвердить, что он работает хорошо. - person Ben; 28.12.2017
comment
Np man :) надеюсь, это сработает. Фреймворк Android иногда отстой :( - person Kushan; 28.12.2017
comment
Я только что видел, как вы отредактировали свой ответ - спасибо за объяснение! На данный момент я использую setAlarmClock() для повтора, но в первом тесте он срабатывал через 6, а не через 5 минут, что на грани нормального, но не очень приятно... может быть. Я попробую другое ваше решение в ближайшее время. (Кроме этого, я все еще хочу протестировать setExact... в течение нескольких дней, прежде чем я отмечу ваш ответ как принятый) - person Ben; 28.12.2017
comment
Давайте продолжим обсуждение в чате. - person Ben; 28.12.2017
comment
Круто, нп, чувак :) не торопитесь .. сигналы тревоги больше никогда не бывают полностью точными, но варианты, которые мы уже используем в ответе, и ваш пост - единственные доступные. Я думаю, вам просто нужно сдаться и верить, что это, по крайней мере, выстрелит. - person Kushan; 28.12.2017
comment
Кажется, сейчас работает хорошо. (Дополнительной проблемой, я думаю, была моя Motorola E4 Plus, которая ОЧЕНЬ глючила на стоковой версии 7.1.1, и иногда часы просто зависали — я полагаю, что это также накапливалось в проблеме позднего будильника… приветствую) - person Ben; 02.01.2018
comment
Кто-нибудь может подтвердить, что setAlarmClock менее точен, чем setExactAndAllowWhileIdle()? - person Yuriy Kulikov; 24.07.2019
comment
только в спящем режиме, в любом другом месте, я думаю, оба варианта одинаково точны - person Kushan; 25.07.2019
comment
@Kushan Что, если я устанавливаю два будильника, скажем, 8:45 и 8:47, так каким должен быть правильный подход, например, я должен использовать setExactAndAllowWhileIdle для первого и setAlarmClock для второго ??? - person syed_noorullah; 18.10.2019
comment
На самом деле с устройствами Motorola может быть что-то не так — на моем Motorola Moto One с Android 10 даже будильники, установленные стандартным приложением будильника, иногда задерживаются (обычно до 5 минут). Я протестировал все доступные способы установки будильника, и наилучшие результаты дает метод setAlarmClock (см. ответ @badadin). - person Derek K; 01.02.2021

В документации указано следующее:

  • #P2#
  • #P3#

В разделе «Адаптируйте приложение к Doze» также говорится, что:

Примечание. Ни setAndAllowWhileIdle(), ни setExactAndAllowWhileIdle() не могут активировать сигналы тревоги более одного раза в 9 минут для каждого приложения.

Документация для setExactAndAllowWhileIdle говорит, что:

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

Так что setExactAndAllowWhileIdle не гарантирует точное выполнение, а setAlarmClock точнее (согласно документации).

person badadin    schedule 16.02.2020