Android AlarmManager - RTC_WAKEUP vs ELAPSED_REALTIME_WAKEUP

87

Alguém pode me explicar a diferença entre AlarmManager.RTC_WAKEUPe AlarmManager.ELAPSED_REALTIME_WAKEUP? Eu li a documentação, mas ainda não entendo realmente a implicação de usar um em vez do outro.

Código de exemplo:

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

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

Quão diferentes serão as duas linhas de código executadas? Quando essas duas linhas de código serão executadas em relação uma à outra?

Eu aprecio sua ajuda.

Camille Sévigny
fonte

Respostas:

140

AlarmManager.ELAPSED_REALTIME_WAKEUP tipo é usado para acionar o alarme desde o tempo de inicialização:

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

irá realmente fazer o alarme disparar 10 minutos após a inicialização do dispositivo .

Existe um cronômetro que começa a funcionar quando o dispositivo inicializa para medir o tempo de atividade do dispositivo e este é o tipo que dispara o seu alarme de acordo com o tempo de atividade do dispositivo.

Considerando que, AlarmManager.RTC_WAKEUPirá disparar o alarme de acordo com a hora do relógio. Por exemplo, se você:

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

isso, por outro lado, disparará o alarme daqui a 30 segundos .

AlarmManager.ELAPSED_REALTIME_WAKEUPtipo raramente é usado em comparação com AlarmManager.RTC_WAKEUP.

Rejinderi
fonte
1
Isso foi o que eu pensei. Eu só precisava de alguma confirmação. Então, se eu fizesse algo assim: alarmManager.set (AlarmManager.ELAPSED_REALTIME_WAKEUP, System.currentTimeMills () + 30 * 1000, pendingIntent); coisas estranhas podem acontecer (que é o que eu tinha até perceber que não estava funcionando como eu pensava.
Camille Sévigny
2
Observe que o código deve ser em System.currentTimeMillis()vez de System.currentTimeMills():)
HasanAboShally
17
"O tipo AlarmManager.ELAPSED_REALTIME_WAKEUP raramente é usado em comparação com AlarmManager.RTC_WAKEUP." Isso é especulação e conselho ruim. De acordo com a documentação: developer.android.com/training/scheduling/alarms.html "Se você simplesmente precisa que seu alarme dispare em um determinado intervalo (por exemplo, a cada meia hora), use um dos tipos de tempo real decorrido. geral, esta é a melhor escolha. "
Jared Kells
1
A resposta de @mborsuk é melhor e corrigindo esta resposta: Você não deve usar RTC para o tempo decorrido, como para thirtySecondsFromNow.
Micha F.
2
Explicação muito boa :)! Eu gostaria de adicionar um comentário importante: em relação aos documentos oficiais do Android, usar "AlarmManager.ELAPSED_REALTIME_WAKEUP" pode ser algo interessante quando se trata de um aplicativo que dispara solicitações HTTP para um servidor, e você não deseja gerar nenhuma carregar no seu servidor web. Pense em milhares de dispositivos Android, executando um GET em um servidor da web, às 22h30: devido ao fato de que funciona durante o tempo de inicialização do dispositivo, "AlarmManager.ELAPSED_REALTIME_WAKEUP" poderia fazer esses "milhares" dispositivos dispararem solicitações, ao mesmo tempo, evitando carregar :).
ivanleoncz
107

Apesar da resposta atualmente aceita e votada, AlarmManager.ELAPSED_REALTIME * digita junto com SystemClock.elapsedRealtime () sempre foi mais confiável do que os relógios RTC para alarmes e tempo.

O uso de ELAPSED_REALTIME_WAKEUP com AlarmManager dependerá de um relógio monotônico começando na hora da inicialização " e continua a funcionar mesmo quando a CPU está em modos de economia de energia, então é a base recomendada para cronometragem de intervalo de uso geral ". Então,

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

fará seu PendingIntent disparar em 1 min (60 * 1000 milissegundos).

Considerando que AlarmManager.RTC_WAKEUP é o tempo de "parede" padrão em milissegundos desde a época. Então,

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

também pode disparar o alarme daqui a 60 segundos, mas não de maneira confiável, porque conforme observado na documentação do SystemClock :

O relógio de parede pode ser definido pelo usuário ou pela rede telefônica (consulte setCurrentTimeMillis (long)), de modo que o tempo pode retroceder ou avançar de maneira imprevisível. Este relógio só deve ser usado quando a correspondência com datas e horas do mundo real for importante, como em um aplicativo de calendário ou despertador. As medições de intervalo ou tempo decorrido devem usar um relógio diferente. Se você estiver usando System.currentTimeMillis (), considere ouvir as transmissões de intent ACTION_TIME_TICK, ACTION_TIME_CHANGED e ACTION_TIMEZONE_CHANGED para descobrir quando o horário muda.

Além disso, a questão referia-se apenas aos alarmes * _WAKEUP, mas consulte também a documentação do AlarmManager para ter certeza de que entendeu o que os alarmes de ativação versus não ativação fornecem.

mborsuk
fonte
Sua resposta é ótima, mas em meu aplicativo, eu precisava definir alarmes que coincidissem com a data / hora do mundo real e não apenas 60 segundos a partir de agora. Nesse caso, RTC_WAKEUP é a melhor solução para mim.
Camille Sévigny
8
Tudo bem, mas é uma resposta mais precisa à pergunta e corrige as coisas na resposta aceita atualmente.
mborsuk
+1 para esta informação, mas observe que elapsedRealtime()está abaixo de em SystemClockvez de System. EDITAR: ... Limite de votos diários atingido ...
Jorge Fuentes González
Essa é uma das melhores respostas do problema. Eu estava procurando por uma agulha em um palheiro de 50 m de altura (também conhecido como "um bug no meu código!"), E sua resposta me levou direto para a agulha!
AlxDroidDev
17

Apenas uma nota. Você pode obter os milis de tempo de atividade chamando:

long uptimeMillis =  SystemClock.elapsedRealtime();

Portanto, se você deseja disparar o alarme daqui a 30 segundos e deseja usar o relógio de tempo de atividade em vez do relógio normal, pode fazer:

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

Sempre que você quiser verificar algum tempo decorrido em vez de uma data / hora específica, é melhor usar o tempo de atividade. Isso porque a hora atual definida pelo usuário no dispositivo pode mudar se o usuário alterá-la usando as configurações.

Ena
fonte
3
1 para indicar o uso de "sempre que quiser verificar o tempo decorrido". Faz sentido perfeitamente.
sulai
Mais uma vez, só para enfatizar: meu entendimento é que isso definitivamente irá disparar daqui a 30 segundos, a menos que algo aconteça como o desligamento do dispositivo. Concretamente, acho que o problema de usar RTC_WAKEUP é que, em teoria, daqui a 15 segundos, é possível que se torne algo como 1h da manhã em algum sábado próximo ao final de outubro e o relógio do sistema volte 1 hora (nos EUA e em grande parte Europa, pelo menos), então o alarme não dispara até 1 hora e 30 segundos após ter sido definido.
Yannick,
2

Eu programei este problema em meu próprio projeto desta forma. no código abaixo que estou usando

AlarmManager.ELAPSED_REALTIME_WAKEUP

para definir o alarme em um horário específico. a variável 'intentName' é usada no intentFilter para receber este alarme. porque estou disparando muitos alarmes desse tipo. quando eu cancelo todos os alarmes. eu uso o método cancel. dado na parte inferior.

// para manter alarmes e cancelar quando necessário

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

onde a função span é esta:

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

função de cancelamento de alarme é esta.

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
fonte
1

Algumas observações importantes ao escolher qual alarme usar : (para quem já leu os votos favoráveis)

O RTC_WAKEUP vale da morte - mudança de tempo:
Se o usuário alterou manualmente o tempo para o passado, o alarme não disparará, e o futuro fará com que o alarme toque imediatamente se ele passar do RTCtimestamp.
Não use este alarme para fazer qualquer verificação do lado do cliente / trabalhos importantes porque ele pode falhar.

O WAKEUP significado (Marshmallow e acima)
Em geral - não muito. Não vai despertar o dispositivo quando idleou enquanto estiver dentro doze, por isso alarmManager.setExactAndAllowWhileIdleou alarmManager.setAndAllowWhileIdle( Soneca e inatividade )

Nir Duan
fonte