Як перевірити, чи в 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 (контекст, MyClass.class), але, схоже, це не працює. Він завжди повертається до нуля, навіть коли будильник працює.
toc777

5
toc777, це не повинно бути рядком, який відповідає заявленій дії у вашому фільтрі намірів у вашому маніфесті.xml
Кріс Найт,

4
Кріс, Це було ще одним питанням, яке викликало мою проблему. Намір, про який я згадав вище, насправді спрацьовує :)
toc777

41
Зауважте, що вам потрібно буде зателефонувати alarmManager.cancel(pendingIntent)і pendingIntent.cancel()в обох випадках , щоб це рішення повернуло помилку.
Кевін Купер

26
Якщо це не очевидно, код у цій відповіді не підтверджує, що очікуваний намір був зареєстрований у диспетчері сигналізації. Код просто підтверджує, що PendingIntent був створений через getBroadcast з еквівалентним цільовим наміром. Ви можете довести це, запустивши код alarmUp після getBroadcast усіх, але перед усіма календарями та диспетчером сигналів. Це повернеться правдою. Цей факт пояснює, чому вам потрібно PendingIntent.cancel, щоб отримати значення, щоб повернутися до false. Строго кажучи, це не відповідає на питання.
bigh_29

114

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

Використовуйте adb shell dumpsys alarm

Ви можете знати, що сигнал тривоги встановлений, і коли вони будуть тривожні та інтервали. Також скільки разів ця сигналізація була викликана.


36
Насправді не програмна відповідь на ОП, а класна порада. Дуже добре знати.
JustSomeGuy

2
додайте грейп, щоб відфільтрувати зазвичай довгий список тривог: adb shell dumpsys alarm | grep <e.g. package name of your app>Також працює на нових системах Windows (я використовую Win10)
muetzenflo

3
grep виконується на мобільному пристрої, а не на вашому ПК. Тож якщо grep працює, залежить від ОС Android. Старі телефони не надходять із жагою.
Хеннінг

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 (та сама операція , та сама інтенція - дія, дані, категорії, компоненти, прапори ), він отримає PendingIntent, що представляє той же маркер, якщо це все-таки є дійсним, і таким чином можна зателефонувати cancel (), щоб видалити його.

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


1
Я не впевнений, що цього достатньо. У тому випадку, коли PendingIntent зареєстровано в AlarmManager і потім зупиниться обома методами скасування, 'isWorking' вище буде як і раніше. Схоже, PendingIntent не був видалений з AlarmManager, і він продовжує повертати екземпляр. Як ми тоді ефективно дізнаємось, коли тривогу було включено / вимкнено?
johnDisplayClass

Це насправді спрацювало чудово. Необхідно зазначити: setAction () та requestCode () повинні бути однаковими у всіх getBroadcast () і варто видалити додаток із вашого пристрою. Це мене вигнало. Дякую
johnDisplayClass

Чудово працює. Дякую!
Амбран

1
Хороший приклад, але я не використовував би 1001 як приватний код запиту. Всього 0, щоб зробити приклад більш очевидним.
Кріс

1
Будь ласка, утримуйтесь від використання "верхньої відповіді" тощо. Натомість надайте посилання на відповідь. Тому що відповіді можуть змінювати позиції на сторінці залежно від популярності.
Катір

44

Зверніть увагу на цю цитату з документів для встановленого методу Менеджера тривоги:

Якщо для цього наміру вже є сигнал тривоги (при рівності двох намірів, визначених Intent.filterEquals), він буде видалений та замінений цим.

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

Вам потрібен інший підхід, якщо ви намагаєтеся обчислити, скільки часу залишається на попередньо створену тривогу, або якщо вам дійсно потрібно знати, чи існує така тривога. Щоб відповісти на ці запитання, спробуйте зберегти спільні попередні дані під час створення сигналу. Ви можете зберігати часові позначки годинника в момент встановлення сигналу тривоги, час, коли очікується, що сигнал триває, і період повторення (якщо встановити сигнал, що повторюється).


2
На мою думку, це має бути прийнятою відповіддю. Якщо тільки в ОП немає особливої ​​ситуації, яка виправдовує не
перезапускати

У моєму випадку я хочу знати, чи сигналізація вже встановлена, і якщо так, я не хочу створювати нову або скидати існуючу тривогу.
Імран Аслам

2
Чудова відповідь. Чому ОП взагалі не перевірила це? Нічого не потрібно робити.
Vijay Kumar Kanta

2
У цьому рішенні є багато отворів у циклі, це може перекрити створений раніше час тривоги (скажімо, якщо час потрібно вказати, як t + 24), тому кожен раз, коли програма запускається, тривожний час продовжує рухатися вперед стан, який він ніколи не може отримати тригер для багатьох, тому перевірка тривоги, якщо її вже існує, є більш надійною
Нага,

10

У мене 2 сигнали тривоги. Я використовую умисел із додатками замість дії, щоб ідентифікувати події:

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

річ у тому, що з різними додатками наміри (і тривоги) не будуть унікальними. Щоб визначити, який сигнал тривоги активний чи ні, я повинен був визначити requestCodeрозріз.

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);

Використовуючи додаткові наміри і це рішення працювало на мене. Лише одна зміна - я використовую послугу, тому я змінив її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);
    }

Іноді на Marshmallow після примусового припинення програми getBroadcast () поверне ненулеве значення, але сигнал не встановлений.
hopia

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


Якщо код запиту є ідентифікатором, чи важливо передати наміри відповідній дії?
Sekula1991

Чи є спосіб отримати час, за який було заплановано тривогу, з програмою тривоги, якщо у вас є певний намір?
М. Сміт

4

Я створив простий (дурний чи ні) сценарій bash, який витягує довги з оболонки 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 перевірить значення наміру, що очікує, воно відповідає дійсності, оскільки AlarmManager оновить прапор очікуваного наміру.

            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 щось подібне.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.