Android AlarmManager - RTC_WAKEUP contro ELAPSED_REALTIME_WAKEUP


87

Qualcuno può spiegarmi la differenza tra AlarmManager.RTC_WAKEUPe AlarmManager.ELAPSED_REALTIME_WAKEUP? Ho letto la documentazione ma ancora non capisco davvero le implicazioni dell'uso dell'una sull'altra.

Codice di esempio:

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

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

In che misura verranno eseguite le due righe di codice? Quando verranno eseguite quelle due righe di codice l'una rispetto all'altra?

Apprezzo il vostro aiuto.

Risposte:


140

AlarmManager.ELAPSED_REALTIME_WAKEUP il tipo viene utilizzato per attivare l'allarme dall'avvio:

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

effettivamente farà suonare l'allarme 10 minuti dopo l' avvio del dispositivo .

C'è un timer che inizia a funzionare all'avvio del dispositivo per misurare il tempo di attività del dispositivo e questo è il tipo che fa scattare l'allarme in base al tempo di attività del dispositivo.

Considerando che, AlarmManager.RTC_WAKEUPattiverà l'allarme in base all'ora dell'orologio. Ad esempio se fai:

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

questo, invece, attiverà l'allarme tra 30 secondi .

AlarmManager.ELAPSED_REALTIME_WAKEUPil tipo è usato raramente rispetto a AlarmManager.RTC_WAKEUP.


1
È quello che pensavo. Avevo solo bisogno di una conferma. Quindi, se ho fatto qualcosa del genere: alarmManager.set (AlarmManager.ELAPSED_REALTIME_WAKEUP, System.currentTimeMills () + 30 * 1000, pendingIntent); Potrebbero accadere cose strane (che è quello che ho avuto fino a quando non ho notato che non funzionava come pensavo.
Camille Sévigny,

2
Si prega di notare che il codice dovrebbe essere System.currentTimeMillis()invece di System.currentTimeMills():)
HasanAboShally

17
"Il tipo AlarmManager.ELAPSED_REALTIME_WAKEUP è usato raramente rispetto a AlarmManager.RTC_WAKEUP." Questa è speculazione e cattivi consigli. Secondo la documentazione: developer.android.com/training/scheduling/alarms.html "Se hai semplicemente bisogno che l'allarme si attivi a un determinato intervallo (ad esempio, ogni mezz'ora), utilizza uno dei tipi di tempo reale trascorso. In generale, questa è la scelta migliore ".
Jared Kells

1
La risposta di @ mborsuk è migliore e correggendo questa risposta: non dovresti usare RTC per il tempo trascorso, come per thirtySecondsFromNow.
Micha F.

2
Bella spiegazione :)! Vorrei aggiungere un commento importante: riguardo ai documenti ufficiali di Android, l'utilizzo di "AlarmManager.ELAPSED_REALTIME_WAKEUP" potrebbe essere qualcosa di interessante quando si tratta di un'applicazione che invia richieste HTTP a un server e non si desidera generarne nessuna caricare sul tuo server web. Pensa a migliaia di dispositivi Android, che eseguono un GET su un server web, alle 22:30: poiché funziona durante il tempo di avvio del dispositivo, "AlarmManager.ELAPSED_REALTIME_WAKEUP" potrebbe fare in modo che questi "migliaia" dispositivi attivino richieste, non allo stesso tempo, evitando il carico :).
ivanleoncz

107

Nonostante la risposta attualmente accettata e votata, i tipi AlarmManager.ELAPSED_REALTIME * insieme a SystemClock.elapsedRealtime () sono sempre stati più affidabili degli orologi RTC per allarmi e tempi.

L'utilizzo di ELAPSED_REALTIME_WAKEUP con AlarmManager si baserà su un orologio monotono a partire dal momento dell'avvio " e continua a funzionare anche quando la CPU è in modalità di risparmio energetico, quindi è la base consigliata per la temporizzazione dell'intervallo per scopi generali ". Così,

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

farà il tuo PendingIntent fuoco in 1 min (60 * 1000 millisecondi).

Al contrario, AlarmManager.RTC_WAKEUP è per il tempo standard "muro" in millisecondi dall'epoca. Così,

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

può anche attivare l'allarme tra 60 secondi da adesso, ma non in modo affidabile, perché come indicato nella documentazione di SystemClock :

L'orologio da parete può essere impostato dall'utente o dalla rete telefonica (vedere setCurrentTimeMillis (lungo)), quindi l'ora può saltare avanti o indietro in modo imprevedibile. Questo orologio deve essere utilizzato solo quando è importante la corrispondenza con date e ore del mondo reale, ad esempio in un'applicazione di calendario o sveglia. Le misurazioni dell'intervallo o del tempo trascorso dovrebbero utilizzare un orologio diverso. Se stai usando System.currentTimeMillis (), considera l'ascolto delle trasmissioni ACTION_TIME_TICK, ACTION_TIME_CHANGED e ACTION_TIMEZONE_CHANGED Intent per scoprire quando cambia l'ora.

Inoltre, la domanda faceva riferimento solo agli allarmi * _WAKEUP, ma consulta anche la documentazione di AlarmManager su questo per assicurarti di aver compreso cosa forniscono gli allarmi di sveglia e non di sveglia.


La tua risposta è ottima, ma nella mia applicazione avevo bisogno di impostare allarmi che coincidessero con la data / l'ora del mondo reale e non solo tra 60 secondi da adesso. In tal caso, RTC_WAKEUP è la soluzione migliore per me.
Camille Sévigny

8
Va bene, ma questa è una risposta più accurata alla domanda e corregge le cose nella risposta attualmente accettata.
mborsuk

+1 per queste informazioni, ma nota che elapsedRealtime()è sotto SystemClockinvece di System. EDIT: ... Limite di voti giornaliero raggiunto ...
Jorge Fuentes González

Questa è una delle migliori risposte nel problema lì. Stavo cercando un ago in un pagliaio alto 50 metri (noto anche come "un bug nel mio codice!"), E la tua risposta mi ha portato direttamente all'ago!
AlxDroidDev

17

Solo una nota. Puoi ottenere la chiamata in millis di uptime:

long uptimeMillis =  SystemClock.elapsedRealtime();

Quindi, se vuoi attivare l'allarme tra 30 secondi da adesso e vuoi usare l'orologio di uptime invece dell'orologio normale, puoi fare:

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

Ogni volta che si desidera controllare un po 'di tempo trascorso invece di una data / ora specifica, è meglio utilizzare il tempo di attività. Questo perché l'ora corrente impostata dall'utente nel dispositivo può cambiare se l'utente la modifica utilizzando le impostazioni.


3
+1 per aver sottolineato di usare quello "ogni volta che vuoi controllare il tempo trascorso". Ha perfettamente senso.
sulai

Ancora una volta, solo per sottolineare: la mia comprensione è che questo si attiverà sicuramente tra 30 secondi a meno che non accada qualcosa come lo spegnimento del dispositivo. In concreto, penso che il problema con l'utilizzo di RTC_WAKEUP sia che in teoria, a 15 secondi da adesso, è possibile che diventi qualcosa come l'una di notte di qualche sabato verso la fine di ottobre e l'orologio di sistema vada indietro di 1 ora (negli Stati Uniti e in gran parte Europa, almeno), quindi l'allarme non si attiva effettivamente fino a 1 ora e 30 secondi dopo che è stato impostato.
Yannick

2

Ho programmato questo problema nel mio progetto in questo modo. nel codice sottostante che sto usando

AlarmManager.ELAPSED_REALTIME_WAKEUP

per impostare la sveglia a un'ora specifica. la variabile "intentName" viene utilizzata nell'intentFilter per ricevere questo allarme. perché sparo molti allarmi di questo tipo. quando annullo tutti gli allarmi. io uso il metodo cancel. dato in fondo.

// per bloccare gli allarmi e annullarli quando necessario

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

dove la funzione span è questa:

 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 funzione di cancellazione dell'allarme è questa.

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

1

Alcune note importanti nella scelta dell'allarme da utilizzare : (per chi ha già letto i voti positivi)

La RTC_WAKEUP valle della morte - cambio di ora:
se l'utente ha modificato manualmente l'ora in base al passato, l'allarme non si attiverà e il futuro farà sì che l'allarme si attivi immediatamente se supera il RTCtimestamp.
Non utilizzare questo allarme per eseguire verifiche lato client / lavori importanti perché potrebbe non riuscire.

Il WAKEUP significato (Marshmallow e sopra)
In generale, non molto. Non si riattiverà il dispositivo quando idleo mentre è dentro doze, per quello alarmManager.setExactAndAllowWhileIdleo alarmManager.setAndAllowWhileIdle( Doze & Idle )

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.