Wie überprüfe ich, ob AlarmManager bereits einen Alarm eingestellt hat?

231

Wenn meine App gestartet wird, soll überprüft werden, ob ein bestimmter Alarm (über AlarmManager registriert) bereits eingestellt ist und ausgeführt wird. Die Ergebnisse von Google scheinen darauf hinzudeuten, dass dies nicht möglich ist. Ist das noch richtig? Ich muss diese Überprüfung durchführen, um den Benutzer zu informieren, bevor Maßnahmen zum Erstellen eines neuen Alarms ergriffen werden.

Ron
quelle
4
Bitte validieren Sie die Antwort, die Ihr Problem gelöst hat, oder veröffentlichen Sie Ihre eigene Lösung.
Anis

Antworten:

322

Im Anschluss an den Kommentar, den Ron gepostet hat, finden Sie hier die detaillierte Lösung. Angenommen, Sie haben einen sich wiederholenden Alarm mit einer ausstehenden Absicht wie folgt registriert:

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

Sie können überprüfen, ob es aktiv ist:

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

Der Schlüssel hier ist der FLAG_NO_CREATE, wie im Javadoc beschrieben: if the described PendingIntent **does not** already exists, then simply return null(anstatt einen neuen zu erstellen)

Chris Knight
quelle
9
Muss die Absicht nur mit einer Aktionszeichenfolge verwendet werden? Ich habe versucht, eine Klasse anzugeben, new Intent (Kontext, MyClass.class), aber es scheint nicht zu funktionieren. Es wird immer null zurückgegeben, auch wenn der Alarm ausgeführt wird.
toc777
5
toc777, nein, es muss ein String sein, der einer deklarierten Aktion in Ihrem Intent-Filter in Ihrer manifest.xml entspricht
Chris Knight
4
Chris, es war ein weiteres Problem, das mein Problem verursachte. Die Absicht, die ich oben erwähnt habe, funktioniert tatsächlich :)
toc777
41
Beachten Sie, dass Sie beide aufrufen müssen werden alarmManager.cancel(pendingIntent)und pendingIntent.cancel()um diese Lösung zu return false.
Kevin Cooper
26
Falls dies nicht offensichtlich ist, überprüft der Code in dieser Antwort nicht, ob die ausstehende Absicht beim Alarmmanager registriert wurde. Der Code überprüft lediglich, ob der PendingIntent über getBroadcast mit einer entsprechenden Zielabsicht erstellt wurde. Sie können dies beweisen, indem Sie den alarmUp-Code nach getBroadcast all, aber vor allen Kalender- und Alarmmanager-Dingen ausführen. Es wird true zurückgeben. Diese Tatsache erklärt, warum Sie PendingIntent.cancel ausführen müssen, um den Wert auf false zurückzusetzen. Genau genommen beantwortet dies die Frage nicht.
bigh_29
114

Für andere, die dies benötigen, ist hier eine Antwort.

Verwenden adb shell dumpsys alarm

Sie können wissen, dass der Alarm eingestellt wurde und wann sie alarmiert werden und in welchem ​​Intervall. Auch wie oft dieser Alarm ausgelöst wurde.

Jack Feng
quelle
36
Keine programmatische Antwort auf das OP, aber ein cooler Tipp. Sehr gut zu wissen.
JustSomeGuy
2
füge ein grep hinzu, um die normalerweise lange Liste von Alarmen zu filtern: Funktioniert adb shell dumpsys alarm | grep <e.g. package name of your app>auch auf neuen Windows-Systemen (ich benutze Win10)
muetzenflo
3
grep wird auf dem mobilen Gerät ausgeführt, nicht auf Ihrem PC. Ob grep funktioniert, hängt also vom Android-Betriebssystem ab. Ältere Telefone werden nicht mit grep geliefert.
Henning
53

Arbeitsbeispiel mit Empfänger (die beste Antwort war nur mit Aktion).

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

Es ist erwähnenswert:

Wenn die erstellende Anwendung später (Prozess) dieselbe Art von PendingIntent (dieselbe Operation , dieselbe Absicht - Aktion, Daten, Kategorien, Komponenten, Flags ) erneut abruft, erhält sie eine PendingIntent, die dasselbe Token darstellt, wenn dies noch gültig ist, und kann daher cancel () aufrufen, um es zu entfernen.

Kurz gesagt, Ihr PendingIntent sollte über dieselben Funktionen (Operation und Intent-Struktur) verfügen, um die Kontrolle darüber zu übernehmen.

toter Fisch
quelle
1
Ich bin mir nicht sicher, ob das ausreicht. In dem Fall, in dem ein PendingIntent beim AlarmManager registriert und dann mit beiden Abbruchmethoden gestoppt wird, ist 'isWorking' oben immer noch wahr. Der PendingIntent wurde anscheinend nicht aus dem AlarmManager entfernt und gibt weiterhin eine Instanz zurück. Woher wissen wir dann effektiv, wann Alarme ein- und ausgeschaltet wurden?
JohnDisplayClass
Das hat tatsächlich perfekt funktioniert. Zu beachten: setAction () und requestCode () müssen in allen getBroadcast () identisch sein, und es lohnt sich, die App von Ihrem Gerät zu deinstallieren. Das hat mich erwischt. Danke
johnDisplayClass
Funktioniert super. Vielen Dank!
Ambran
1
Schönes Beispiel, aber ich würde 1001 dort nicht als privaten Anforderungscode verwenden. Nur 0, um das Beispiel deutlicher zu machen.
Chris
1
Bitte verwenden Sie keine "Top-Antwort" usw. Geben Sie stattdessen einen Link zur Antwort an. Weil Antworten die Position auf der Seite je nach Beliebtheit ändern können.
Kathir
44

Beachten Sie dieses Zitat aus den Dokumenten für die festgelegte Methode des Alarm Managers:

Wenn für diese Absicht bereits ein Alarm geplant ist (wobei die Gleichheit von zwei Absichten durch Intent.filterEquals definiert wird), wird er entfernt und durch diesen ersetzt.

Wenn Sie wissen, dass der Alarm eingestellt werden soll, müssen Sie nicht prüfen, ob er bereits vorhanden ist oder nicht. Erstellen Sie es einfach jedes Mal, wenn Ihre App startet. Sie werden alle früheren Alarme durch dieselben ersetzen Intent.

Sie benötigen einen anderen Ansatz, wenn Sie berechnen möchten, wie viel Zeit für einen zuvor erstellten Alarm noch verbleibt, oder wenn Sie wirklich wissen müssen, ob ein solcher Alarm überhaupt vorhanden ist. Um diese Fragen zu beantworten, sollten Sie zum Zeitpunkt der Erstellung des Alarms freigegebene Voreinstellungsdaten speichern. Sie können den Zeitstempel der Uhr zum Zeitpunkt der Alarmeinstellung, die Zeit, zu der der Alarm ausgelöst werden soll, und die Wiederholungsperiode (wenn Sie einen Wiederholungsalarm einrichten) speichern.

bigh_29
quelle
2
Meiner Meinung nach sollte dies die akzeptierte Antwort sein. Es sei denn, das OP hat eine spezielle Situation, die es rechtfertigt, den Alarm nicht zu wiederholen
Jose_GD
In meinem Fall möchte ich wissen, ob der Alarm bereits eingestellt ist, und wenn ja, möchte ich keinen neuen erstellen oder den vorhandenen Alarm zurücksetzen.
Imran Aslam
2
Hervorragende Antwort. Warum hat das OP das überhaupt nicht überprüft? Sie müssen nichts tun.
Vijay Kumar Kanta
2
Diese Lösung enthält viele Lücken. Dies kann die zuvor erstellte Alarmzeit überschreiben (z. B. wenn die Zeit wie t + 24 angegeben werden muss), sodass bei jedem Start der App die Alarmzeit einen Zustand vorwärts bewegt, den sie niemals erreichen könnte Auslöser für viele, so dass die Überprüfung des Alarms, wenn es bereits existiert, zuverlässiger ist
Naga
10

Ich habe 2 Alarme. Ich verwende Absichten mit Extras anstelle von Aktionen, um die Ereignisse zu identifizieren:

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

Die Sache ist, dass mit Diff-Extras die Absicht (und der Alarm) nicht eindeutig sein wird. Um zu erkennen, welcher Alarm aktiv ist oder nicht, musste ich diff requestCode-s definieren :

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

und so wurde Alarm erzeugt:

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
quelle
Die Verwendung der Vorsatz-Extras und dieser Lösung hat bei mir funktioniert. Nur eine Änderung ist, dass ich den Dienst benutze, also habe ich ihn geändert inPendingIntent.getService
Pankaj
8

Habe gerade eine andere Lösung gefunden, es scheint für mich zu funktionieren

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
quelle
Manchmal gibt getBroadcast () auf Marshmallow nach dem erzwungenen Stoppen einer App ungleich Null zurück, aber der Alarm ist nicht gesetzt.
Hopia
6

Während fast jeder hier die richtige Antwort gegeben hat, hat niemand erklärt, auf welcher Grundlage die Alarme funktionieren

Sie können tatsächlich mehr darüber erfahren , AlarmManagerund seine Arbeit hier . Aber hier ist die schnelle Antwort

Sie sehen im AlarmManagerGrunde Zeitpläne a PendingIntentirgendwann in der Zukunft. Um den geplanten Alarm abzubrechen, müssen Sie den Alarm abbrechen PendingIntent.

Beachten Sie beim Erstellen des immer zwei Dinge PendingIntent

PendingIntent.getBroadcast(context,REQUEST_CODE,intent, PendingIntent.FLAG_UPDATE_CURRENT);
  • Anforderungscode - Dient als eindeutige Kennung
  • Flag - Definiert das Verhalten von PendingIntent

Um zu überprüfen, ob der Alarm bereits geplant ist, oder um den Alarm abzubrechen, müssen Sie nur noch auf denselben zugreifen PendingIntent. Dies kann erfolgen, wenn Sie denselben Anforderungscode verwenden und FLAG_NO_CREATEwie unten gezeigt verwenden

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

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

Mit FLAG_NO_CREATEwird zurückgegeben, nullwenn das PendingIntentnoch nicht existiert. Wenn es bereits vorhanden ist, wird ein Verweis auf das vorhandene zurückgegebenPendingIntent

IrshadKumail
quelle
Wenn der Anforderungscode eine Kennung ist, ist es wichtig, die Absicht mit der passenden Aktion zu übergeben?
Sekula1991
Gibt es eine Möglichkeit, die Zeit, für die der Alarm geplant war, mit dem Alarmanager abzurufen, wenn Sie die ausstehende Absicht haben?
M. Smith
4

Ich habe ein einfaches (dummes oder nicht dummes) Bash-Skript erstellt, das die Longs aus der ADB-Shell extrahiert, sie in Zeitstempel konvertiert und rot anzeigt.

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

Versuch es ;)

Jan Ni
quelle
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 erstellt keine ausstehende Absicht, sodass der boolesche Wert false angegeben wird.

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

Nachdem der AlarmManager den Wert von Pending Intent überprüft hat, gibt er true aus, da AlarmManager das Flag für Pending Intent aktualisiert.

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

            }
MIkka Marmik
quelle
0

Ich habe den Eindruck, dass es keine Möglichkeit gibt, dies zu tun, aber es wäre schön.

Sie können ein ähnliches Ergebnis erzielen, indem Sie irgendwo eine Alarm_last_set_time aufzeichnen lassen und einen On_boot_starter BroadcastReciever: BOOT_COMPLETED.

Stephen
quelle