Каков правильный способ реализации сигнала тревоги, который запускается один раз, но остается активным после перезагрузки?

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

В моей основной деятельности

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

На моей службе.

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

Вот в чем проблема

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

Мое решение (не элегантное).

После того, как сообщение отправляется в первый раз, я добавляю 50 лет (в миллисекундах) к времени будильника и сохраняю его постоянно. Таким образом, он не будет запускаться снова, пока пользователь не установит время самостоятельно. Кажется, это сработало, но ИМО — ужасное решение.

Должен быть более правильный способ настроить постоянное оповещение так, чтобы оно запускалось только один раз. Может ли кто-нибудь, у кого больше опыта работы с AlarmManager, посоветовать мне?


person BriCo84    schedule 11.07.2013    source источник
comment
Вы используете AlarmManager? Вы очищаете время будильника из постоянного хранилища после срабатывания будильника?   -  person Karakuri    schedule 11.07.2013
comment
Почему нельзя отменить будильник после отправки сообщения?   -  person codeMagic    schedule 11.07.2013
comment
Да, я использую AlarmManager. Неважно, сбрасываю ли я время будильника, потому что, если оно равно нулю, будильник, когда он снова устанавливается (при загрузке, а затем проверяет время будильника, на какое миллисекунды его установить), сработает сразу же, начиная с теперь я добавляю к этому времени 50 лет в миллисекундах, чтобы оно не срабатывало. Я не могу отменить будильник, потому что это бесполезно. Помните, чтобы настроить будильник, чтобы он не отключался при перезагрузке, я должен запустить его из службы, которая запускается при загрузке. поэтому он всегда будет запускать этот код при загрузке, чтобы создать сигнал тревоги   -  person BriCo84    schedule 11.07.2013
comment
Тревога настроена на запуск службы, которая по умолчанию запускается при загрузке. Таким образом, даже если я отменю будильник после его срабатывания, при следующей перезагрузке телефона он запустит эту службу и воссоздаст будильник.   -  person BriCo84    schedule 11.07.2013


Ответы (3)


Вы можете зарегистрировать BroadcastReciever onBooted, чтобы снова запустить будильник, но вам определенно нужно сохранить время будильника в общих настройках или базе данных sqlite...

прошел через то же самое раньше

person Lukas Olsen    schedule 11.07.2013
comment
Пожалуйста, перечитайте вопрос. Проблема не в том, что он не сохраняет при загрузке. это спасает. Я использую широковещательный приемник. - person BriCo84; 11.07.2013

Вам лучше сделать что-то вроде использования AlarmManager для доставки PendingIntent в установленное время, которое запускает IntentService, которое доставляет ваше сообщение и завершается, когда сообщение отправлено. Вы бы сбросили AlarmManager с помощью PendingIntent в своем BroadcastReceiver, который прослушивает BOOT_COMPLETED. Нехорошо, чтобы ваша служба работала все время, и она может быть убита (и сделать ее приоритетной службой, чтобы уменьшить вероятность ее уничтожения, было бы пустой тратой ресурсов).

person HexAndBugs    schedule 11.07.2013
comment
Хорошо, но как настроить отложенное намерение на срабатывание в определенное время? Разве мне не нужно ставить будильник для этого? И если это так, если я хочу, чтобы он не умирал, если телефон перезагрузится до того, как он сработает, я должен запустить эту службу при загрузке, верно? Так что я в том же положении, что и раньше. - person BriCo84; 11.07.2013
comment
Вам нужен AlarmManager, чтобы настроить PendingIntent на срабатывание в определенное время, и вы сбрасываете AlarmManager с помощью PendingIntent, если телефон перезагружается до того, как он сработает. Ваш BroadcastReceiver, который обнаруживает завершение загрузки, может запустить IntentService, который либо отправляет сообщение, либо сбрасывает AlarmManager. IntentService имеет огромное преимущество, заключающееся в том, что он не работает все время, поэтому он намного чище, чем исходное решение. Кроме того, как заявляли другие, вам просто нужен флаг, чтобы определить, было ли отправлено сообщение или нет, что вы должны проверить в IntentService. - person HexAndBugs; 12.07.2013

Когда вызывается служба (которая после первого планирования сообщения теперь работает постоянно)

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

он создает будильник с сохраненным временем будильника

Я предполагаю, исходя из контекста, что «создает сигнал тревоги» означает «планирует событие с AlarmManager». Если вы используете AlarmManager, нет причин иметь службу, которая «всегда работает».

Я не вижу способа отключить эту службу от постоянной работы

Во-первых, он не должен был «всегда работать». При этом, чтобы остановить службу, служба может вызвать stopSelf().

Поэтому, как только служба будет убита и перезапущена (например, при перезагрузке телефона), она запустится, увидит, что время будильника уже в прошлом, и сразу же запустит службу, которая отправляет сообщение.

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

После того, как сообщение отправляется в первый раз, я добавляю 50 лет (в миллисекундах) к времени будильника и сохраняю его постоянно. Таким образом, он не будет запускаться снова, пока пользователь не установит время самостоятельно. Кажется, это сработало, но ИМО — ужасное решение.

Вы можете использовать hasBeenDelivered boolean или эквивалент. Или, в зависимости от вашей структуры данных, полностью удалите запись. Независимо от того, ваша постоянная модель данных должна отражать статус отправленного/неотправленного сообщения, как бы вы ни решили это сделать.

person CommonsWare    schedule 11.07.2013
comment
Спасибо за Ваш ответ. Я думал об использовании логического флага для отправки/не отправки, но я видел, что это ничем не отличается от того, что я уже делал. На самом деле, я просто смотрю на это и думаю, должен же быть более элегантный и правильный способ сделать это. В ответ на то, что служба для создания диспетчера аварийных сигналов всегда запущена, как я это вижу, я должен всегда запускать ее и при загрузке. потому что, если приложение будет уничтожено или телефон перезагрузится, сигналы тревоги AlarmManager для этого приложения будут уничтожены, поэтому его необходимо перезапустить и сбросить сигнал тревоги, если это произойдет. Я что-то упускаю? - person BriCo84; 11.07.2013
comment
@BriCo84: Я что-то пропустил? -- вы ошибаетесь в своем утверждении, что аварийные сигналы AlarmManager убиваются для этого приложения. Это происходит только в том случае, если пользователь принудительно останавливает вас (например, через настройки), и в этом случае у вас большие проблемы. должен быть более элегантный, правильный способ сделать это - наличие точной модели данных, показывающей состояние событий, казалось бы, элегантным, правильным способом сделать это. - person CommonsWare; 11.07.2013
comment
О, я думал, что будильники отключаются, если система Android также убивает приложение, моя ошибка. Но проблема с перезагрузкой действительно проблема. Если телефон перезагрузится до того, как сработает будильник, он будет убит/отменен. - person BriCo84; 11.07.2013
comment
@BriCo84: Если телефон перезагрузится до того, как сработает будильник, он будет убит/отменен - ​​абсолютно. Вот почему ваша загрузка BroadcastReceiver может запустить IntentService, которая может проверить ваше постоянное хранилище данных, посмотреть, есть ли ожидающие сообщения, и запланировать для них аварийные сигналы. IntentService дает вам фоновый поток, плюс сервис уходит, как только эта работа завершается, так что не забивает память. Пока вы поддерживаете свою постоянную модель данных в актуальном состоянии в отношении новых и завершенных сообщений, вы должны быть настроены. - person CommonsWare; 11.07.2013
comment
Хорошо, это имеет смысл, и это немного чище, чем то, как я это делаю сейчас. Таким образом, вместо того, чтобы увеличивать время будильника и иметь будильник, но не иметь значения (50 лет в будущем), используйте логическое значение, чтобы определить, нужно ли вообще создавать будильник. Я предполагаю, что это лучшее решение, но я все же думаю, что должен быть (или должен быть) более простой способ аккуратно сработать одноразовый будильник. - person BriCo84; 11.07.2013
comment
@BriCo84: используйте логическое значение, чтобы определить, нужно ли вообще создавать будильник - да. Это в основном эквивалент флага чтения в почтовом клиенте или приложении для чтения каналов и т. д. Я думаю, что это лучшее решение, но я все еще думаю, что должен быть (или должен быть) более простой способ запустить один раз сигнал тревоги чисто - если это недостаточно чисто, то вам не повезло. - person CommonsWare; 11.07.2013