Android AlarmManager - RTC_WAKEUP vs ELAPSED_REALTIME_WAKEUP

87

Quelqu'un peut-il m'expliquer la différence entre AlarmManager.RTC_WAKEUPet AlarmManager.ELAPSED_REALTIME_WAKEUP? J'ai lu la documentation mais je ne comprends toujours pas vraiment les implications de l'utilisation de l'un par rapport à l'autre.

Exemple de code:

    alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 
                     scheduledAlarmTime, 
                     pendingIntent);

    alarmManager.set(AlarmManager.RTC_WAKEUP, 
                     scheduledAlarmTime, 
                     pendingIntent);

Dans quelle mesure les deux lignes de code seront-elles différentes? Quand ces deux lignes de code s'exécuteront-elles l'une par rapport à l'autre?

J'apprécie ton aide.

Camille Sévigny
la source

Réponses:

140

AlarmManager.ELAPSED_REALTIME_WAKEUP type est utilisé pour déclencher l'alarme depuis le démarrage:

alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 600000, pendingIntent);

fera retentir l'alarme 10 minutes après le démarrage de l'appareil .

Il y a une minuterie qui commence à fonctionner lorsque l'appareil démarre pour mesurer le temps de disponibilité de l'appareil et c'est le type qui déclenche votre alarme en fonction de la disponibilité de l'appareil.

Alors que, AlarmManager.RTC_WAKEUPdéclenchera l'alarme en fonction de l'heure de l'horloge. Par exemple si vous faites:

long thirtySecondsFromNow = System.currentTimeMillis() + 30 * 1000;
alarmManager.set(AlarmManager.RTC_WAKEUP, thirtySecondsFromNow , pendingIntent);

ceci, par contre, déclenchera l'alarme dans 30 secondes .

AlarmManager.ELAPSED_REALTIME_WAKEUPle type est rarement utilisé par rapport à AlarmManager.RTC_WAKEUP.

Rejinderi
la source
1
C'est ce que je pensais. J'avais juste besoin d'une confirmation. Donc, si je faisais quelque chose comme ceci: alarmManager.set (AlarmManager.ELAPSED_REALTIME_WAKEUP, System.currentTimeMills () + 30 * 1000, pendingIntent); des choses étranges peuvent arriver (c'est ce que j'avais jusqu'à ce que je remarque que ça ne fonctionnait pas comme je le pensais.
Camille Sévigny
2
Veuillez noter que le code doit être System.currentTimeMillis()au lieu de System.currentTimeMills():)
HasanAboShally
17
"Le type AlarmManager.ELAPSED_REALTIME_WAKEUP est rarement utilisé par rapport à AlarmManager.RTC_WAKEUP." C'est de la spéculation et de mauvais conseils. Selon la documentation: developer.android.com/training/scheduling/alarms.html "Si vous avez simplement besoin que votre alarme se déclenche à un intervalle particulier (par exemple, toutes les demi-heures), utilisez l'un des types temps réel écoulé. Dans général, c'est le meilleur choix. "
Jared Kells
1
La réponse de @ mborsuk est meilleure et corrige cette réponse: vous ne devriez pas utiliser RTC pendant le temps écoulé, comme pour thirtySecondsFromNow.
Micha F.
2
Très bonne explication :)! Je voudrais ajouter un commentaire important: concernant les documents officiels d'Android, utiliser "AlarmManager.ELAPSED_REALTIME_WAKEUP" pourrait être quelque chose d'intéressant quand il s'agit d'une application qui envoie des requêtes HTTP à un serveur, et vous ne voulez pas en générer charge sur votre serveur Web. Pensez à des milliers d'appareils Android, effectuant un GET sur un serveur Web, à 22h30: en raison du fait que cela fonctionne pendant le démarrage de l'appareil, "AlarmManager.ELAPSED_REALTIME_WAKEUP" pourrait faire que ces "milliers" d'appareils lancent des requêtes, dans le même temps, en évitant de charger :).
ivanleoncz
107

Malgré la réponse actuellement acceptée et votée à la hausse, les types AlarmManager.ELAPSED_REALTIME * avec SystemClock.elapsedRealtime () ont toujours été plus fiables que les horloges RTC pour les alarmes et le chronométrage.

L'utilisation de ELAPSED_REALTIME_WAKEUP avec AlarmManager s'appuiera sur une horloge monotone à partir de l'heure de démarrage " et continue à cocher même lorsque le processeur est en mode d'économie d'énergie, il en va de même pour la base recommandée pour la synchronisation d'intervalle à usage général ". Donc,

alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()
                 + 60*1000, pendingIntent);

fera feu votre PendingIntent en 1 min (60 * 1000 millisecondes).

Alors que, AlarmManager.RTC_WAKEUP est pour le temps "mur" standard en millisecondes depuis l'époque. Donc,

alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
                 + 60*10000, pendingIntent);

peut également déclencher l'alarme dans 60 secondes, mais pas de manière fiable, car comme indiqué dans la documentation SystemClock :

L'horloge murale peut être réglée par l'utilisateur ou par le réseau téléphonique (voir setCurrentTimeMillis (long)), de sorte que l'heure peut sauter en arrière ou en avant de manière imprévisible. Cette horloge ne doit être utilisée que lorsque la correspondance avec les dates et heures du monde réel est importante, par exemple dans une application de calendrier ou de réveil. Les mesures d'intervalle ou de temps écoulé doivent utiliser une horloge différente. Si vous utilisez System.currentTimeMillis (), envisagez d'écouter les diffusions d'intention ACTION_TIME_TICK, ACTION_TIME_CHANGED et ACTION_TIMEZONE_CHANGED pour savoir quand l'heure change.

De plus, la question ne faisait référence qu'aux alarmes * _WAKEUP, mais consultez également la documentation AlarmManager à ce sujet pour vous assurer que vous comprenez ce que fournissent les alarmes de réveil et de non-réveil.

mborsuk
la source
Votre réponse est excellente, mais dans mon application, j'avais besoin de définir des alarmes qui coïncidaient avec la date / l'heure du monde réel et pas seulement 60 secondes à partir de maintenant. Dans ce cas, RTC_WAKEUP est la meilleure solution pour moi.
Camille Sévigny
8
C'est bien, mais c'est une réponse plus précise à la question et corrige les choses dans la réponse actuellement acceptée.
mborsuk
+1 pour cette information, mais notez que elapsedRealtime()c'est sous SystemClockau lieu de System. EDIT: ... Limite de vote quotidienne atteinte ...
Jorge Fuentes González
C'est l'une des meilleures réponses au problème là-bas. Je cherchais une aiguille dans une botte de foin de 50 m de haut (c'est-à-dire "un bug dans mon code!"), Et ta réponse m'a conduit directement à l'aiguille!
AlxDroidDev
17

Juste une note. Vous pouvez obtenir les millis de disponibilité en appel:

long uptimeMillis =  SystemClock.elapsedRealtime();

Donc, si vous souhaitez déclencher l'alarme dans 30 secondes à partir de maintenant et que vous souhaitez utiliser l'horloge de disponibilité au lieu de l'horloge normale, vous pouvez faire:

long thirtySecondsFromNow =  SystemClock.elapsedRealtime() + 30 * 1000;
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, thirtySecondsFromNow, pendingIntent);

Chaque fois que vous souhaitez vérifier un certain temps écoulé au lieu d'une date / heure spécifique, il est préférable d'utiliser le temps de disponibilité. En effet, l'heure actuelle définie par l'utilisateur dans l'appareil peut changer si l'utilisateur la modifie à l'aide des paramètres.

Ena
la source
3
+1 pour indiquer d'utiliser cela "chaque fois que vous voulez vérifier le temps écoulé". C'est parfaitement logique.
sulai
Encore une fois, juste pour souligner: je crois comprendre que cela se déclenchera certainement dans 30 secondes à moins que quelque chose ne se produise comme l'arrêt de l'appareil. Concrètement, je pense que le problème avec l'utilisation de RTC_WAKEUP est qu'en théorie, dans 15 secondes, il est possible que cela devienne quelque chose comme 1h du matin un samedi vers la fin d'octobre et que l'horloge système recule d'une heure (aux États-Unis et dans une grande partie Europe, au moins), de sorte que l'alarme ne se déclenche réellement que 1 heure et 30 secondes après son déclenchement.
Yannick
2

J'ai programmé ce problème dans mon propre projet de cette façon. dans le code ci-dessous que j'utilise

AlarmManager.ELAPSED_REALTIME_WAKEUP

pour régler l'alarme à une heure précise. la variable 'intentName' est utilisée dans intentFilter pour recevoir cette alarme. parce que je déclenche de nombreuses alarmes de ce type. lorsque j'annule toutes les alarmes. j'utilise la méthode annuler. donné en bas.

// pour maintenir les alarmes et annuler si nécessaire

     public static ArrayList<String> alarmIntens = new ArrayList<String>();

//

    public static String setAlarm(int hour, int minutes, long repeatInterval,
        final Context c) {
    /*
     * to use elapsed realTime monotonic clock, and fire alarm at a specific time
     * we need to know the span between current time and the time of alarm.
     * then we can add this span to 'elapsedRealTime' to fire the alarm at that time
     * this way we can get alarms even when device is in sleep mood
    */
    Time nowTime = new Time();
    nowTime.setToNow();
    Time startTime = new Time(nowTime);
    startTime.hour = hour;
    startTime.minute = minutes;
    //get the span from current time to alarm time 'startTime'
    long spanToStart = TimeUtils.spanInMillis(nowTime, startTime);
    //
    intentName = "AlarmBroadcast_" + nowTime.toString();
    Intent intent = new Intent(intentName);
    alarmIntens.add(intentName);
    PendingIntent pi = PendingIntent.getBroadcast(c, alarms++, intent,
            PendingIntent.FLAG_UPDATE_CURRENT);
    //
    AlarmManager am = (AlarmManager) c
            .getSystemService(Context.ALARM_SERVICE);
    //adding span to elapsedRealTime
    long elapsedRealTime = SystemClock.elapsedRealtime();
    Time t1 = new Time();
    t1.set(elapsedRealTime);
    t1.second=0;//cut inexact timings, seconds etc
    elapsedRealTime = t1.toMillis(true);

    if (!(repeatInterval == -1))
        am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                elapsedRealTime + spanToStart, repeatInterval, pi);
    else
        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, elapsedRealTime
                + spanToStart, pi);

où la fonction span est la suivante:

 public static long spanInMillis(Time startTime, Time endTime) {
    long diff = endTime.toMillis(true) - startTime.toMillis(true);
    if (diff >= 0)
        return diff;
    else
        return AlarmManager.INTERVAL_DAY - Math.abs(diff);
}

La fonction d'annulation d'alarme est la suivante.

public static void cancel(Context c) {
    AlarmManager am = (AlarmManager) c
            .getSystemService(Context.ALARM_SERVICE);
    // cancel all alarms
    for (Iterator<String> iterator = alarmIntens.iterator(); iterator
            .hasNext();) {
        String intentName = (String) iterator.next();
        // cancel
        Intent intent = new Intent(intentName);
        PendingIntent pi = PendingIntent.getBroadcast(c, 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        am.cancel(pi);
        //
        iterator.remove();
    }
}
ahmadalibaloch
la source
1

Quelques notes importantes lors du choix de l'alarme à utiliser : (pour qui qui a déjà lu les votes positifs)

La RTC_WAKEUP vallée de la mort - changement d'heure:
si l'utilisateur a manuellement changé l'heure au passé, l'alarme ne se déclenchera pas, et le futur provoquera l'alarme immédiatement si elle dépasse l' RTChorodatage.
N'utilisez pas cette alarme pour effectuer une vérification côté client / des tâches importantes car elle risque d'échouer.

Le WAKEUP sens (guimauve et plus)
En général - pas beaucoup. Ne réveillera pas l'appareil quand idleou pendant qu'il est doze, pour cela alarmManager.setExactAndAllowWhileIdleou alarmManager.setAndAllowWhileIdle( Doze & Idle )

Nir Duan
la source