Come verificare se AlarmManager ha già impostato un allarme?


231

All'avvio della mia app, voglio che controlli se un determinato allarme (registrato tramite AlarmManager) è già impostato e in esecuzione. I risultati di Google sembrano indicare che non c'è modo di farlo. È ancora corretto? Devo fare questo controllo per avvisare l'utente prima di intraprendere qualsiasi azione per creare un nuovo allarme.


4
Convalida la risposta che ha risolto il problema o pubblica la tua soluzione.
Anis,

Risposte:


322

A seguito del commento pubblicato, ecco la soluzione dettagliata. Supponiamo che tu abbia registrato un allarme ripetuto con un intento in sospeso come questo:

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

Il modo in cui verifichi se è attivo è:

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

La chiave qui è la FLAG_NO_CREATEquale come descritto nel javadoc: if the described PendingIntent **does not** already exists, then simply return null(invece di crearne uno nuovo)


9
Deve usare l'intento con solo una stringa di azioni? Ho provato a specificare una classe, un nuovo intento (contesto, MyClass.class) ma non sembra funzionare. Restituisce sempre null anche quando l'allarme è in esecuzione.
toc777,

5
toc777, no, deve essere una stringa che corrisponde a un'azione dichiarata nel filtro intenti in manifest.xml
Chris Knight,

4
Chris, è stato un altro problema a causare il mio problema. L'intento che ho menzionato sopra funziona davvero :)
toc777

41
Nota che dovrai chiamare entrambi alarmManager.cancel(pendingIntent)e pendingIntent.cancel()affinché questa soluzione restituisca false.
Kevin Cooper,

26
Nel caso in cui non sia ovvio, il codice in questa risposta non verifica che l'intento in sospeso sia stato registrato con il gestore degli allarmi. Il codice verifica semplicemente che PendingIntent è stato creato tramite getBroadcast con un obiettivo di destinazione equivalente. Puoi provarlo eseguendo il codice alarmUp dopo getBroadcast tutto, ma prima di tutto il calendario e le cose del gestore degli allarmi. Tornerà vero. Questo fatto spiega perché è necessario PendingIntent.cancel per ottenere il valore per tornare a false. A rigor di termini, questo non risponde alla domanda.
bigh_29,

114

Per gli altri che potrebbero aver bisogno di questo, ecco una risposta.

Uso adb shell dumpsys alarm

Puoi sapere che la sveglia è stata impostata e quando stanno andando su allarme e intervallo. Inoltre, quante volte questo allarme è stato invocato.


36
Non proprio una risposta programmatica all'OP, ma un bel consiglio. Molto bene a sapersi.
JustSomeGuy,

2
aggiungi un grep per filtrare l'elenco solitamente lungo di allarmi: adb shell dumpsys alarm | grep <e.g. package name of your app>funziona anche su nuovi sistemi Windows (io uso Win10)
muetzenflo

3
grep viene eseguito sul dispositivo mobile, non sul PC. Quindi, se grep funziona dipende dal sistema operativo Android. I telefoni meno recenti non sono dotati di grep.
Henning

53

Esempio funzionante con ricevitore (la risposta principale era solo con l'azione).

//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...");

Vale la pena ricordare:

Se l'applicazione di creazione (processo) successiva recupera lo stesso tipo di PendingIntent (stessa operazione , stessi Intenti - azione, dati, categorie, componenti, flag ), riceverà un PendingIntent che rappresenta lo stesso token se è ancora valido e può quindi chiamare cancel () per rimuoverlo.

In breve, PendingIntent dovrebbe avere le stesse funzionalità (operazione e struttura dell'intento) per assumerne il controllo.


1
Non sono sicuro che questo sia sufficiente. Nel caso in cui un PendingIntent sia registrato con AlarmManager e quindi arrestato con entrambi i metodi di annullamento, 'isWorking' sopra sarà comunque vero. PendingIntent sembra non essere stato rimosso da AlarmManager e continuerà a restituire un'istanza. Come possiamo quindi sapere efficacemente quando gli allarmi sono stati attivati ​​/ disattivati?
johnDisplayClass

Questo in realtà ha funzionato perfettamente. Cose da notare: setAction () e requestCode () devono essere identici in tutti i getBroadcast () e vale la pena disinstallare l'app dal tuo dispositivo. Mi ha sorpreso. Grazie
johnDisplayClass l'

Funziona alla grande. Grazie!
Ambran,

1
Bell'esempio, ma non userei 1001 come codice di richiesta privato lì. Solo 0 per rendere l'esempio più ovvio.
Chris,

1
Si prega di astenersi dall'utilizzare la "risposta migliore" ecc. Fornire invece un collegamento alla risposta. Perché le risposte possono cambiare le posizioni sulla pagina in base alla popolarità.
Kathir,

44

Nota questa citazione dai documenti per il metodo impostato di Alarm Manager:

Se esiste già un allarme per questo Intento programmato (con l'uguaglianza di due intenti definito da Intent.filterEquals), verrà rimosso e sostituito da questo.

Se sai di voler impostare la sveglia, non devi preoccuparti di controllare se esiste già o meno. Crealo ogni volta che la tua app si avvia. Sostituirai tutti gli allarmi passati con gli stessi Intent.

È necessario un approccio diverso se si sta tentando di calcolare quanto tempo è rimasto su un allarme precedentemente creato o se è davvero necessario sapere se tale allarme esiste. Per rispondere a queste domande, considera di salvare i dati pref condivisi nel momento in cui crei l'allarme. È possibile memorizzare il timestamp dell'orologio nel momento in cui è stata impostata la sveglia, l'ora in cui si prevede che la sveglia si spenga e il periodo di ripetizione (se si imposta una sveglia ripetuta).


2
Secondo me questa dovrebbe essere la risposta accettata. A meno che l'OP non abbia una situazione speciale che giustifica il mancato ripristino dell'allarme
Jose_GD

Nel mio caso, voglio sapere se l'allarme è già impostato e, in tal caso, non voglio crearne uno nuovo o ripristinare l'allarme esistente.
Imran Aslam,

2
Risposta superba. Perché l'OP non l'ha verificato affatto? Non c'è niente che tu debba fare.
Vijay Kumar Kanta,

2
Ci sono molti buchi loop in questa soluzione, questo può sovrascrivere l'ora della sveglia creata in precedenza (diciamo se il tempo deve essere specificato come t + 24) quindi ogni volta che l'app viene lanciata l'ora della sveglia continua a spostarsi in uno stato che non potrebbe mai ottenere innescare per molti, quindi controllare se l'allarme è già esistente è più affidabile
Naga,

10

Ho 2 allarmi. Sto usando intento con extra invece di azione per identificare gli eventi:

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

il fatto è che con gli extra diff l'intento (e l'allarme) non saranno unici. Quindi, per poter identificare quale allarme è attivo o meno, ho dovuto definire le differenze requestCode:

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

ed ecco come è stato creato l'allarme:

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

Usando gli extra intenti e questa soluzione ha funzionato per me. Solo una modifica è che sto usando il servizio, quindi l'ho cambiato inPendingIntent.getService
Pankaj,

8

Ho appena trovato un'altra soluzione, sembra funzionare per me

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

A volte, su Marshmallow, dopo aver forzato l'arresto di un'app, getBroadcast () restituisce un valore non nullo, ma l'allarme non viene impostato.
hopia,

6

Mentre quasi tutti qui hanno dato la risposta corretta, nessun organismo ha spiegato su quali basi funzionano gli allarmi

Puoi effettivamente saperne di più AlarmManagere come funziona qui . Ma ecco la risposta rapida

In AlarmManagerpratica vedrai pianificazioni PendingIntenta qualche ora in futuro. Quindi, per annullare l'allarme programmato è necessario annullare il PendingIntent.

Prendi sempre nota di due cose durante la creazione di PendingIntent

PendingIntent.getBroadcast(context,REQUEST_CODE,intent, PendingIntent.FLAG_UPDATE_CURRENT);
  • Codice richiesta: funge da identificatore univoco
  • Flag: definisce il comportamento di PendingIntent

Ora per verificare se l'allarme è già programmato o per annullare l'allarme, è sufficiente accedere alla stessa PendingIntent. Questo può essere fatto se si utilizza lo stesso codice di richiesta e si utilizza FLAG_NO_CREATEcome mostrato di seguito

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

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

Con FLAG_NO_CREATEesso tornerà nullse il PendingIntentnon esiste già. Se esiste già, restituisce riferimento all'esistentePendingIntent


Se il codice di richiesta è un identificatore, è importante passare l'intento con l'azione corrispondente?
Sekula1991,

C'è un modo per ottenere l'orario in cui l'allarme era programmato con la sveglia se si ha l'intenzione in sospeso?
M. Smith,

4

Ho creato un semplice script bash (stupido o no), che estrae i long dalla shell adb, li converte in timestamp e lo mostra in rosso.

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

Provalo ;)


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 non viene creato con l'intento in sospeso in modo che dia un valore booleano falso.

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

Dopo che AlarmManager verifica il valore di Intento in sospeso, questo diventa vero perché AlarmManager aggiorna la bandiera dell'intento in sospeso.

            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

Ho l'impressione che non ci sia modo di farlo, sarebbe bello però.

È possibile ottenere un risultato simile registrando un Alarm_last_set_time da qualche parte e avendo un On_boot_starter BroadcastReciever: BOOT_COMPLETED in qualche modo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.