Comment vérifier si AlarmManager a déjà une alarme définie?

231

Lorsque mon application démarre, je souhaite qu'elle vérifie si une alarme particulière (enregistrée via AlarmManager) est déjà définie et en cours d'exécution. Les résultats de Google semblent indiquer qu'il n'y a aucun moyen de le faire. Est-ce toujours correct? Je dois effectuer cette vérification afin d'avertir l'utilisateur avant toute action pour créer une nouvelle alarme.

Ron
la source
4
Veuillez valider la réponse qui a résolu votre problème ou publier votre propre solution.
Anis

Réponses:

322

Suite au commentaire publié, voici la solution détaillée. Supposons que vous ayez enregistré une alarme répétitive avec une intention en attente comme celle-ci:

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

La façon dont vous vérifieriez s'il est actif est la suivante:

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 clé ici est celle FLAG_NO_CREATEqui, comme décrit dans le javadoc: if the described PendingIntent **does not** already exists, then simply return null(au lieu d'en créer un nouveau)

Chris Knight
la source
9
Doit-il utiliser l'intention avec juste une chaîne d'action? J'ai essayé de spécifier une classe, une nouvelle intention (context, MyClass.class) mais cela ne semble pas fonctionner. Il retourne toujours null même lorsque l'alarme est en cours d'exécution.
toc777
5
toc777, non, il doit s'agir d'une chaîne qui correspond à une action déclarée dans votre filtre d'intention dans votre manifest.xml
Chris Knight
4
Chris, c'était un autre problème qui causait mon problème. L'intention que j'ai mentionnée ci-dessus fonctionne réellement :)
toc777
41
Notez que vous devrez appeler les deux alarmManager.cancel(pendingIntent)et pendingIntent.cancel()pour que cette solution retourne false.
Kevin Cooper du
26
Si ce n'est pas évident, le code de cette réponse ne vérifie pas que l'intention en attente a été enregistrée auprès du gestionnaire d'alarmes. Le code vérifie simplement que le PendingIntent a été créé via getBroadcast avec une intention cible équivalente. Vous pouvez le prouver en exécutant le code alarmUp après le getBroadcast all, mais avant tout le calendrier et le gestionnaire d'alarmes. Cela reviendra vrai. Ce fait explique pourquoi vous devez PendingIntent.cancel pour que la valeur revienne à false. À proprement parler, cela ne répond pas à la question.
bigh_29
114

Pour ceux qui pourraient en avoir besoin, voici une réponse.

Utilisation adb shell dumpsys alarm

Vous pouvez savoir que l'alarme a été réglée et quand vont-ils déclencher une alarme et un intervalle. Aussi combien de fois cette alarme a été invoquée.

Jack Feng
la source
36
Pas vraiment une réponse programmatique à l'OP, mais une astuce sympa. Très bon à savoir.
JustSomeGuy
2
ajouter un grep pour filtrer la liste d'alarmes généralement longue: adb shell dumpsys alarm | grep <e.g. package name of your app>Fonctionne également sur les nouveaux systèmes Windows (j'utilise Win10)
muetzenflo
3
grep est exécuté sur l'appareil mobile, pas sur votre PC. Donc, si grep fonctionne, cela dépend du système d'exploitation Android. Les téléphones plus anciens ne sont pas livrés avec grep.
Henning
53

Exemple de travail avec récepteur (la première réponse était juste avec action).

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

Il convient de mentionner:

Si l'application qui crée plus tard (processus) récupère le même type de PendingIntent (même opération , même intention - action, données, catégories, composants, indicateurs ), elle recevra un PendingIntent représentant le même jeton si celui-ci est toujours valide, et peut donc appeler cancel () pour le supprimer.

En bref, votre PendingIntent devrait avoir les mêmes fonctionnalités (structure d'opération et d'intention) pour en prendre le contrôle.

poisson mort
la source
1
Je ne suis pas sûr que ce soit suffisant. Dans le cas où un PendingIntent est enregistré avec AlarmManager puis arrêté par les deux méthodes d'annulation, «isWorking» ci-dessus sera toujours vrai. Le PendingIntent ne semble pas avoir été supprimé de AlarmManager et continuera de renvoyer une instance. Comment savoir alors efficacement quand les alarmes ont été activées / désactivées?
johnDisplayClass
En fait, cela a parfaitement fonctionné. Choses à noter: setAction () et requestCode () doivent être identiques dans tous les getBroadcast () et cela vaut la peine de désinstaller l'application de votre appareil. Cela m'a rattrapé. Merci
johnDisplayClass
Fonctionne très bien. Merci!
Ambran
1
Bel exemple mais je n'utiliserais pas 1001 comme code de demande privé là-bas. Juste 0 pour rendre l'exemple plus évident.
Chris
1
Veuillez vous abstenir d'utiliser "top answer" etc. Parce que les réponses peuvent changer de position sur la page en fonction de la popularité.
Kathir
44

Notez cette citation des documents pour la méthode définie de Alarm Manager:

S'il y a déjà une alarme pour cette intention planifiée (avec l'égalité de deux intentions définie par Intent.filterEquals), elle sera supprimée et remplacée par celle-ci.

Si vous savez que vous voulez régler l'alarme, vous n'avez pas besoin de vous soucier de vérifier si elle existe déjà ou non. Il suffit de le créer à chaque démarrage de votre application. Vous remplacerez toutes les alarmes passées par les mêmes Intent.

Vous avez besoin d'une approche différente si vous essayez de calculer le temps restant sur une alarme créée précédemment, ou si vous avez vraiment besoin de savoir si une telle alarme existe même. Pour répondre à ces questions, pensez à enregistrer les données pref partagées au moment où vous créez l'alarme. Vous pouvez stocker l'horodatage de l'horloge au moment où l'alarme a été définie, l'heure à laquelle vous vous attendez à ce que l'alarme se déclenche et la période de répétition (si vous configurez une alarme répétitive).

bigh_29
la source
2
À mon avis, cela devrait être la réponse acceptée. À moins que l'OP ait une situation particulière qui justifie de ne pas répéter l'alarme
Jose_GD
Dans mon cas, je veux savoir si l'alarme est déjà réglée et si c'est le cas, je ne veux pas en créer une nouvelle ou réinitialiser l'alarme existante.
Imran Aslam
2
Superbe réponse. Pourquoi l'OP n'a-t-il pas vérifié cela du tout? Vous n'avez rien à faire.
Vijay Kumar Kanta
2
Il existe de nombreux trous de boucle dans cette solution, cela peut remplacer l'heure d'alarme précédemment créée (disons si l'heure doit être spécifiée comme t + 24), donc chaque fois que l'application est lancée, l'heure d'alarme continue d'avancer dans un état qu'elle ne pourrait jamais obtenir déclencher pour beaucoup, donc vérifier l'alarme si elle existe déjà est plus fiable
Naga
10

J'ai 2 alarmes. J'utilise l'intention avec des extras au lieu de l'action pour identifier les événements:

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

le fait est qu'avec les extras diff, l'intention (et l'alarme) ne sera pas unique. Donc pour pouvoir identifier quelle alarme est active ou non, j'ai dû définir des diff requestCode-s:

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

et voici comment l'alarme a été créée:

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
la source
Utiliser les extras d'intention et cette solution a fonctionné pour moi. Un seul changement est que j'utilise le service, je l'ai donc changé enPendingIntent.getService
Pankaj
8

Je viens de trouver une autre solution, cela semble fonctionner pour moi

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
la source
Parfois, sur Marshmallow, après avoir forcé l'arrêt d'une application, getBroadcast () renverra non nul, mais l'alarme n'est pas définie.
hopia
6

Alors que presque tout le monde ici a donné la bonne réponse, aucun organisme n'a expliqué sur quelle base fonctionnent les alarmes

Vous pouvez en fait en savoir plus sur AlarmManagerson fonctionnement ici . Mais voici la réponse rapide

Vous voyez AlarmManageressentiellement des horaires un PendingIntentà un moment donné dans le futur. Donc, pour annuler l'alarme programmée, vous devez annuler la PendingIntent.

Notez toujours deux choses lors de la création du PendingIntent

PendingIntent.getBroadcast(context,REQUEST_CODE,intent, PendingIntent.FLAG_UPDATE_CURRENT);
  • Code de demande - Agit comme l'identifiant unique
  • Indicateur - Définit le comportement de PendingIntent

Maintenant, pour vérifier si l'alarme est déjà programmée ou pour annuler l'alarme, il vous suffit d'y accéder PendingIntent. Cela peut être fait si vous utilisez le même code de demande et utilisez FLAG_NO_CREATEcomme indiqué ci-dessous

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

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

Avec FLAG_NO_CREATEil reviendra nullsi le PendingIntentn'existe pas déjà. S'il existe déjà, il renvoie la référence à l'existantPendingIntent

IrshadKumail
la source
Si le code de demande est un identifiant, est-il important de transmettre l'intention avec l'action correspondante?
Sekula1991
Existe-t-il un moyen d'obtenir l'heure à laquelle l'alarme a été planifiée avec alarmanager si vous avez l'intention en attente?
M. Smith
4

J'ai fait un simple script bash (stupide ou pas), qui extrait les longs du shell adb, les convertit en horodatages et les affiche en rouge.

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

essayez-le;)

Jan Ni
la source
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 ne crée pas d'intention en attente de sorte qu'il donne la valeur booléenne 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);

Après que AlarmManager vérifie la valeur de Pending Intent, il donne la valeur true car AlarmManager met à jour le drapeau de 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");

            }
MIkka Marmik
la source
0

Im sous l'impression qu'il n'y a aucun moyen de le faire, ce serait bien cependant.

Vous pouvez obtenir un résultat similaire en enregistrant un Alarm_last_set_time quelque part et en ayant un truc On_boot_starter BroadcastReciever: BOOT_COMPLETED.

Stephen
la source