Как проверить, если AlarmManager уже имеет установленный будильник?

231

Когда мое приложение запускается, я хочу, чтобы оно проверило, установлен ли и работает ли определенный сигнал тревоги (зарегистрированный с помощью AlarmManager). Результаты от Google, кажется, указывают, что нет никакого способа сделать это. Это все еще правильно? Мне нужно сделать эту проверку, чтобы сообщить пользователю, прежде чем предпринимать какие-либо действия для создания нового сигнала тревоги.

рон
источник
4
Пожалуйста, подтвердите ответ, который решил вашу проблему или опубликуйте собственное решение.
Анис

Ответы:

322

В продолжение комментария Рона, вот подробное решение. Допустим, вы зарегистрировали повторяющуюся тревогу с таким намерением, как это:

Intent intent = new Intent("com.my.package.MY_UNIQUE_ACTION");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 
                                      intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.MINUTE, 1);

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60, pendingIntent);

Чтобы проверить, активен ли он, нужно:

boolean alarmUp = (PendingIntent.getBroadcast(context, 0, 
        new Intent("com.my.package.MY_UNIQUE_ACTION"), 
        PendingIntent.FLAG_NO_CREATE) != null);

if (alarmUp)
{
    Log.d("myTag", "Alarm is already active");
}

Ключ здесь - это, FLAG_NO_CREATEкак описано в javadoc: if the described PendingIntent **does not** already exists, then simply return null(вместо создания нового)

Крис Найт
источник
9
Нужно ли использовать Intent только с Action String? Я попытался указать класс, новый Intent (context, MyClass.class), но он не работает. Он всегда возвращает ноль, даже когда работает будильник.
toc777
5
toc777, нет, это должна быть строка, которая соответствует объявленному действию в вашем фильтре намерений в файле manifest.xml
Крис Найт,
4
Крис, это была другая проблема, которая вызывала мою проблему. Намерение, которое я упомянул выше, действительно работает :)
toc777
41
Обратите внимание, что вам нужно будет вызвать оба alarmManager.cancel(pendingIntent)и pendingIntent.cancel()для того, чтобы это решение вернуло false.
Кевин Купер,
26
Если это не очевидно, код в этом ответе не проверяет, что ожидающее намерение было зарегистрировано в диспетчере аварий. Код просто проверяет, что PendingIntent был создан через getBroadcast с эквивалентным целевым назначением. Вы можете доказать это, запустив код alarmUp после getBroadcast all, но перед всем, что касается календаря и менеджера аварий. Это вернет истину. Этот факт объясняет, почему вы должны PendingIntent.cancel, чтобы получить значение, чтобы вернуться к false. Строго говоря, это не отвечает на вопрос.
bigh_29
114

Для тех, кому это может понадобиться, вот ответ.

использование adb shell dumpsys alarm

Вы можете знать, что будильник был установлен, и когда они будут активированы и интервал. Также, сколько раз эта тревога была вызвана.

Джек Фенг
источник
36
Не совсем программный ответ на ОП, но классный совет. Очень приятно знать.
JustSomeGuy
2
добавьте grep для фильтрации обычно длинного списка аварий: adb shell dumpsys alarm | grep <e.g. package name of your app>также работает на новых системах Windows (я использую Win10)
muetzenflo
3
grep выполняется на мобильном устройстве, а не на вашем компьютере. Так что если grep работает, зависит от ОС Android. Старые телефоны не поставляются с grep.
Хеннинг
53

Рабочий пример с получателем (верхний ответ был просто с действием).

//starting
AlarmManager alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getActivity(), MyReceiver.class);
intent.setAction(MyReceiver.ACTION_ALARM_RECEIVER);//my custom string action name
PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity(), 1001, intent, PendingIntent.FLAG_CANCEL_CURRENT);//used unique ID as 1001
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), aroundInterval, pendingIntent);//first start will start asap

//and stopping
Intent intent = new Intent(getActivity(), MyReceiver.class);//the same as up
intent.setAction(MyReceiver.ACTION_ALARM_RECEIVER);//the same as up
PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity(), 1001, intent, PendingIntent.FLAG_CANCEL_CURRENT);//the same as up
alarmManager.cancel(pendingIntent);//important
pendingIntent.cancel();//important

//checking if alarm is working with pendingIntent
Intent intent = new Intent(getActivity(), MyReceiver.class);//the same as up
intent.setAction(MyReceiver.ACTION_ALARM_RECEIVER);//the same as up
boolean isWorking = (PendingIntent.getBroadcast(getActivity(), 1001, intent, PendingIntent.FLAG_NO_CREATE) != null);//just changed the flag
Log.d(TAG, "alarm is " + (isWorking ? "" : "not") + " working...");

Стоит упомянуть:

Если создаваемое приложение позднее (процесс) повторно извлекает тот же тип PendingIntent (та же операция , то же самое Intent's - действие, данные, категории, компоненты, флаги ), оно получит PendingIntent, представляющий тот же токен, если он все еще действителен, и Таким образом, можно вызвать метод cancel () для его удаления.

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

мертвая рыба
источник
1
Я не уверен, что этого достаточно. В случае, если PendingIntent зарегистрирован в AlarmManager и затем остановлен обоими методами отмены, вышеприведенное «isWorking» все равно будет истинным. PendingIntent, по-видимому, не был удален из AlarmManager и будет продолжать возвращать экземпляр. Как мы тогда эффективно узнаем, когда тревоги были включены / выключены?
johnDisplayClass
Это на самом деле работает отлично. На что следует обратить внимание: setAction () и requestCode () должны быть идентичны во всех getBroadcast (), поэтому стоит удалить приложение с вашего устройства. Это застало меня врасплох. Спасибо
johnDisplayClass
Прекрасно работает. Спасибо!
Амбран
1
Хороший пример, но я бы не использовал 1001 в качестве личного кода запроса. Просто 0, чтобы сделать пример более очевидным.
Крис
1
Пожалуйста, воздержитесь от использования «верхнего ответа» и т. Д. Вместо этого предоставьте ссылку на ответ. Потому что ответы могут менять позиции на странице в зависимости от популярности.
Катир
44

Обратите внимание на эту цитату из документов для метода set диспетчера аварийных сигналов:

Если для этого намерения уже запланирован сигнал тревоги (с равенством двух намерений, определяемых Intent.filterEquals), то он будет удален и заменен этим.

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

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

bigh_29
источник
2
На мой взгляд, это должен быть принятый ответ. Если у ОП нет особой ситуации, которая не оправдывает тревогу
Jose_GD
В моем случае я хочу знать, установлен ли будильник, и если да, я не хочу создавать новый или сбрасывать существующий будильник.
Имран Аслам
2
Превосходный ответ. Почему ОП вообще не проверял это? Там нет ничего, что вам нужно сделать.
Виджай Кумар Канта
2
В этом решении есть много дыр в петлях, это может переопределить ранее созданное время будильника (скажем, если время должно быть указано как t + 24), поэтому каждый раз, когда приложение запускается, время будильника продолжает двигаться вперед в состояние, которое оно никогда не сможет получить триггер для многих, поэтому проверка тревоги, если она уже существует, более надежна
Нага
10

У меня 2 тревоги. Я использую намерение с дополнениями вместо действия, чтобы идентифицировать события:

Intent i = new Intent(context, AppReciever.class);
i.putExtra("timer", "timer1");

Дело в том, что с дополнительными возможностями намерение (и тревога) не будут уникальными. Таким образом, чтобы определить, какая сигнализация активна или нет, мне пришлось определить diff requestCode-s:

boolean alarmUp = (PendingIntent.getBroadcast(context, MyApp.TIMER_1, i, 
                    PendingIntent.FLAG_NO_CREATE) != null);

и вот как была создана тревога:

public static final int TIMER_1 = 1;
public static final int TIMER_2 = 2;

PendingIntent pending = PendingIntent.getBroadcast(context, TIMER_1, i,
            PendingIntent.FLAG_CANCEL_CURRENT);
setInexactRepeating(AlarmManager.RTC_WAKEUP,
            cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pending);
pending = PendingIntent.getBroadcast(context, TIMER_2, i,
            PendingIntent.FLAG_CANCEL_CURRENT);
setInexactRepeating(AlarmManager.RTC_WAKEUP,
            cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pending);
HiB
источник
Использование намерений дополнений, и это решение работало для меня. Единственное изменение - я использую сервис, поэтому я изменил его наPendingIntent.getService
Pankaj
8

Просто нашел другое решение, похоже, у меня работает

Intent myIntent = new Intent(MainActivity.this, MyReceiver.class);

boolean isWorking = (PendingIntent.getBroadcast(MainActivity.this, 0, myIntent, PendingIntent.FLAG_NO_CREATE) != null);
if (isWorking) {Log.d("alarm", "is working");} else {Log.d("alarm", "is not working");}

if(!isWorking) {
    pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, myIntent,    PendingIntent.FLAG_UPDATE_CURRENT);
    alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    int timeNotif = 5 * 60 * 1000;//time in ms, 7*24*60*60*1000 for 1 week
    Log.d("Notif", "Notification every (ms): " + timeNotif);
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), timeNotif, pendingIntent);
    }
user3856297
источник
Иногда в Marshmallow после принудительной остановки приложения функция getBroadcast () возвращает ненулевое значение, но сигнал тревоги не устанавливается.
Хопия
6

В то время как почти все здесь дали правильный ответ, никто не объяснил, на каком основании работают аварийные сигналы.

Вы можете узнать больше о AlarmManagerего работе здесь . Но вот быстрый ответ

Вы видите в AlarmManagerосновном графики PendingIntentна какое-то время в будущем. Таким образом, чтобы отменить запланированный будильник, необходимо отменить PendingIntent.

Всегда помните две вещи при создании PendingIntent

PendingIntent.getBroadcast(context,REQUEST_CODE,intent, PendingIntent.FLAG_UPDATE_CURRENT);
  • Код запроса - действует как уникальный идентификатор
  • Флаг - определяет поведение PendingIntent

Теперь, чтобы проверить, запланирован ли будильник, или отменить будильник, вам просто нужно получить к нему доступ PendingIntent. Это можно сделать, если вы используете тот же код запроса и используете, FLAG_NO_CREATEкак показано ниже

PendingIntent pendingIntent=PendingIntent.getBroadcast(this,REQUEST_CODE,intent,PendingIntent.FLAG_NO_CREATE);

if (pendingIntent!=null)
   alarmManager.cancel(pendingIntent);

С FLAG_NO_CREATEним вернется, nullесли PendingIntentеще не существует. Если он уже существует, он возвращает ссылку на существующийPendingIntent

IrshadKumail
источник
Если код запроса является идентификатором, важно ли передать намерение с соответствующим действием?
Sekula1991
Есть ли способ узнать время, на которое был запланирован сигнал тревоги, с помощью alarmanager, если у вас есть ожидающее намерение?
М. Смит
4

Я сделал простой (глупый или нет) сценарий bash, который извлекает long из оболочки adb, конвертирует их в метки времени и показывает их красным.

echo "Please set a search filter"
read search

adb shell dumpsys alarm | grep $search | (while read i; do echo $i; _DT=$(echo $i | grep -Eo 'when\s+([0-9]{10})' | tr -d '[[:alpha:][:space:]]'); if [ $_DT ]; then echo -e "\e[31m$(date -d @$_DT)\e[0m"; fi; done;)

попытайся ;)

Ян Ни
источник
1
    Intent intent = new Intent("com.my.package.MY_UNIQUE_ACTION");
            PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    sqlitewraper.context, 0, intent,
                    PendingIntent.FLAG_NO_CREATE);

FLAG_NO_CREATE не создает ожидающее намерение, так что оно дает логическое значение false.

            boolean alarmUp = (PendingIntent.getBroadcast(sqlitewraper.context, 0,
                    new Intent("com.my.package.MY_UNIQUE_ACTION"),
                    PendingIntent.FLAG_NO_CREATE) != null);

            if (alarmUp) {
                System.out.print("k");

            }

            AlarmManager alarmManager = (AlarmManager) sqlitewraper.context
                    .getSystemService(Context.ALARM_SERVICE);
            alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
                    System.currentTimeMillis(), 1000 * 60, pendingIntent);

После AlarmManager проверьте значение Pending Intent, оно дает значение true, потому что AlarmManager обновляет флаг Pending Intent.

            boolean alarmUp1 = (PendingIntent.getBroadcast(sqlitewraper.context, 0,
                    new Intent("com.my.package.MY_UNIQUE_ACTION"),
                    PendingIntent.FLAG_UPDATE_CURRENT) != null);
            if (alarmUp1) {
                System.out.print("k");

            }
Микка Мармик
источник
0

Я нахожусь под впечатлением, что нет никакого способа сделать это, хотя было бы неплохо.

Вы можете достичь аналогичного результата, записав где-нибудь Alarm_last_set_time и имея On_boot_starter BroadcastReciever: BOOT_COMPLETED, что-то вроде.

Стивен
источник