Примечание: я пробовал различные решения, о которых написано здесь, в StackOverflow (пример здесь ). Пожалуйста, не закрывайте это, не проверяя, работает ли ваше решение из того, что вы нашли, используя тест, который я написал ниже.
Фон
В приложении есть требование, чтобы пользователь устанавливал напоминание для планирования в определенное время, поэтому, когда приложение запускается в это время, оно выполняет что-то крошечное в фоновом режиме (просто некоторая операция запроса к БД) и показывает простое уведомление, чтобы рассказать о напоминании.
В прошлом я использовал простой код, чтобы установить что-то для планирования в относительно определенное время:
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
when {
VERSION.SDK_INT >= VERSION_CODES.KITKAT -> alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
else -> alarmManager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
}
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("AppLog", "AlarmReceiver onReceive")
//do something in the real app
}
}
Применение:
val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
setAlarm(this, timeToTrigger, 1)
Проблема
Сейчас я проверил этот код на эмуляторах на новых версиях Android и на Pixel 4 с Android 10, и он, похоже, не срабатывает, или, может быть, срабатывает по прошествии очень долгого времени с момента его предоставления. Мне хорошо известно об ужасном поведении, которое некоторые OEM-производители добавили при удалении приложений из недавних задач, но это относится как к эмуляторам, так и к устройству Pixel 4 (в наличии).
Я прочитал в документах о настройке будильника, что он ограничен для приложений, так что это не будет происходить слишком часто, но это не объясняет, как установить будильник в определенное время, и не объясняет почему приложение Google Clock успешно это делает.
Не только это, но, согласно тому, что я понимаю, говорится, что ограничения должны быть применены, особенно для состояния низкого энергопотребления устройства, но в моем случае у меня не было этого состояния, как на устройстве, так и на эмуляторах. Я установил будильник, который должен сработать примерно через минуту.
Видя, что многие приложения-будильники уже не работают, как раньше, я думаю, что в документах чего-то не хватает. Примером таких приложений является популярное приложение Timely , которое было куплено Google, но никогда не получало новых обновлений, чтобы справиться с новыми ограничениями, и теперь пользователи хотят его вернуть. , Тем не менее, некоторые популярные приложения работают нормально, например, это .
Что я пробовал
Чтобы проверить, действительно ли работает аварийный сигнал, я выполняю следующие тесты, пытаясь активировать аварийный сигнал через минуту, после первой установки приложения, пока устройство подключено к ПК (для просмотра журналов):
- Проверьте, когда приложение находится на переднем плане, видимом для пользователя. - заняло 1-2 минуты.
- Проверка того, когда приложение было отправлено в фоновый режим (например, с помощью кнопки «Домой») - заняло около 1 минуты
- Проверьте, когда задача приложения была удалена из последних задач. - Я ждал более 20 минут и не видел срабатывания будильника, записи в журналы.
- Как # 3, но и выключить экран. Наверное, было бы хуже ...
Я пытался использовать следующие вещи, все не работают:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
комбинация любого из вышеперечисленного, с:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
Пытался использовать сервис вместо BroadcastReceiver. Также попробовал другой процесс.
Попытка сделать приложение игнорируемым из-за оптимизации заряда батареи (не помогло), но, поскольку другие приложения не нуждаются в этом, я не должен его использовать.
Пробовал с помощью этого:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- Попытка иметь службу, которая будет иметь триггер onTaskRemoved , чтобы перепланировать тревогу там, но это также не помогло (хотя служба работала нормально).
Что касается приложения Google Clock, я не увидел в нем ничего особенного, за исключением того, что оно показывает уведомление до его запуска, а также я не вижу его в разделе «неоптимизировано» на экране настроек оптимизации батареи.
Видя, что это похоже на ошибку, я сообщил об этом здесь , включая пример проекта и видео, чтобы показать проблему.
Я проверил несколько версий эмулятора, и похоже, что это поведение началось с API 27 (Android 8.1 - Oreo). Глядя на документы , я не вижу упоминания AlarmManager, но вместо этого было написано о различных фоновых работах.
Вопросы
Как нам настроить что-то, что должно быть запущено в относительно точное время в настоящее время?
Почему вышеперечисленные решения больше не работают? Я что-то пропустил? Разрешение? Может быть, я должен использовать Worker вместо этого? Но тогда не значит ли это, что это может вообще не сработать вовремя?
Как приложение Google «Часы» преодолевает все это и в любом случае срабатывает на точное время, всегда, даже если оно было запущено минуту назад? Только потому, что это системное приложение? Что, если оно будет установлено как пользовательское приложение на устройстве, которое не имеет встроенного?
Если вы говорите , что это потому , что это системное приложение, я нашел другое приложение , которое может вызвать тревогу в два раза в течение 2 минут, здесь , хотя я думаю , что он может использовать службу переднего плана иногда.
РЕДАКТИРОВАТЬ: сделал крошечный репозиторий Github, чтобы примерить идеи, здесь .
РЕДАКТИРОВАТЬ: наконец-то нашел образец, который с открытым исходным кодом и не имеет этой проблемы. К сожалению, это очень сложно, и я все еще пытаюсь выяснить, чем он отличается (и каков минимальный код, который я должен добавить в свой POC), который позволяет планировать его тревоги после удаления приложения из недавних задач