Android AlarmManager - RTC_WAKEUP против ELAPSED_REALTIME_WAKEUP

87

Может кто-нибудь объяснить мне разницу между AlarmManager.RTC_WAKEUP и AlarmManager.ELAPSED_REALTIME_WAKEUP? Я прочитал документацию, но до сих пор не совсем понимаю, что означает использование одного над другим.

Пример кода:

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

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

Насколько по-разному будут выполняться две строки кода? Когда эти две строки кода будут выполняться относительно друг друга?

Я ценю вашу помощь.

Камиль Севиньи
источник

Ответы:

140

AlarmManager.ELAPSED_REALTIME_WAKEUP Тип используется для срабатывания сигнализации с момента загрузки:

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

фактически вызовет срабатывание будильника через 10 минут после загрузки устройства .

Существует таймер, который запускается при загрузке устройства, чтобы измерить время безотказной работы устройства, и это тип, который запускает ваш сигнал тревоги в соответствии с временем безотказной работы устройства.

При этом AlarmManager.RTC_WAKEUPсработает будильник по времени на часах. Например, если вы делаете:

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

это, с другой стороны, вызовет тревогу через 30 секунд .

AlarmManager.ELAPSED_REALTIME_WAKEUPtype используется редко по сравнению с AlarmManager.RTC_WAKEUP.

Реджиндери
источник
1
Это то, о чем я думал. Мне просто нужно было подтверждение. Итак, если бы я сделал что-то вроде этого: alarmManager.set (AlarmManager.ELAPSED_REALTIME_WAKEUP, System.currentTimeMills () + 30 * 1000, pendingIntent); могут происходить странные вещи (это то, что у меня было, пока я не заметил, что это не работает, как я думал.
Камилла Севиньи
2
Обратите внимание, что код должен быть System.currentTimeMillis()вместо System.currentTimeMills():)
HasanAboShally
17
«Тип AlarmManager.ELAPSED_REALTIME_WAKEUP используется редко по сравнению с AlarmManager.RTC_WAKEUP». Это домыслы и плохой совет. Согласно документации: developer.android.com/training/scheduling/alarms.html "Если вам просто нужно, чтобы ваш будильник срабатывал с определенным интервалом (например, каждые полчаса), используйте один из типов прошедшего реального времени. в общем, это лучший выбор ".
Джаред Келлс
1
Ответ @mborsuk лучше и исправляет этот ответ: вы не должны использовать RTC для прошедшего времени, например для thirtySecondsFromNow.
Micha F.
2
Довольно хорошее объяснение :)! Я хотел бы добавить важный комментарий: что касается официальных документов от Android, использование «AlarmManager.ELAPSED_REALTIME_WAKEUP» может быть чем-то интересным, когда дело доходит до приложения, которое отправляет HTTP-запросы к серверу, и вы не хотите их генерировать. загрузить на свой веб-сервер. Подумайте о тысячах Android-устройств, выполняющих GET на веб-серверах в 22:30: из-за того, что он работает во время загрузки устройства, «AlarmManager.ELAPSED_REALTIME_WAKEUP» может заставить эти «тысячи» устройств запускать запросы, а не в то же время, избегая нагрузки :).
ivanleoncz 07
107

Несмотря на принятый в настоящее время и одобренный ответ, типы AlarmManager.ELAPSED_REALTIME * вместе с SystemClock.elapsedRealtime () всегда были более надежными, чем часы RTC для сигналов тревоги и времени.

Использование ELAPSED_REALTIME_WAKEUP с AlarmManager будет основываться на монотонных часах, начиная с момента загрузки, « и продолжает тикать, даже когда ЦП находится в режимах энергосбережения, поэтому это рекомендуемая основа для интервалов времени общего назначения ». Так,

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

заставит ваш PendingIntent сработать за 1 минуту (60 * 1000 миллисекунд).

Принимая во внимание, что AlarmManager.RTC_WAKEUP - это стандартное время «стены» в миллисекундах с начала эпохи. Так,

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

может также вызвать тревогу через 60 секунд, но не надежно, потому что, как указано в документации SystemClock :

Настенные часы могут быть установлены пользователем или в телефонной сети (см. SetCurrentTimeMillis (long)), поэтому время может непредсказуемо прыгать назад или вперед. Эти часы следует использовать только в том случае, если важно соответствие с реальными датами и временем, например, в приложении календаря или будильника. Измерения интервалов или прошедшего времени должны выполняться с использованием других часов. Если вы используете System.currentTimeMillis (), рассмотрите возможность прослушивания трансляций намерений ACTION_TIME_TICK, ACTION_TIME_CHANGED и ACTION_TIMEZONE_CHANGED, чтобы узнать, когда меняется время.

Кроме того, вопрос касается только сигналов тревоги * _WAKEUP, но также см. Документацию по AlarmManager , чтобы убедиться, что вы понимаете, что предоставляют сигналы пробуждения и не пробуждающие.

мборсук
источник
Ваш ответ отличный, но в моем приложении мне нужно было установить будильники, которые совпадали с реальной датой / временем, а не только через 60 секунд. В этом случае RTC_WAKEUP - лучшее решение для меня.
Camille Sévigny
8
Это нормально, но это более точный ответ на вопрос, который исправляет положение в принятом на данный момент ответе.
mborsuk
+1 за эту информацию, но обратите внимание, что elapsedRealtime()это под SystemClockвместо System. РЕДАКТИРОВАТЬ: ... Дневной лимит голосов достигнут ...
Хорхе Фуэнтес Гонсалес,
Это один из лучших ответов на этот вопрос. Я искал иголку в стоге сена высотой 50 метров (он же «ошибка в моем коде!»), И ваш ответ привел меня прямо к игле!
AlxDroidDev
17

Просто примечание. Вы можете получить миллисекунд безотказной работы, позвонив:

long uptimeMillis =  SystemClock.elapsedRealtime();

Поэтому, если вы хотите включить будильник через 30 секунд и хотите использовать часы безотказной работы вместо обычных часов, вы можете сделать:

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

Всякий раз, когда вы хотите проверить прошедшее время вместо определенной даты / времени, лучше использовать время безотказной работы. Это связано с тем, что текущее время, установленное пользователем на устройстве, может измениться, если пользователь изменит его с помощью настроек.

Эна
источник
3
+1 за указание на использование этого «всякий раз, когда вы хотите проверить прошедшее время». Совершенно логично.
Сулай
Опять же, чтобы подчеркнуть: я понимаю, что это определенно сработает через 30 секунд, если не произойдет что-то вроде выключения устройства. Конкретно, я думаю, что проблема с использованием RTC_WAKEUP заключается в том, что теоретически через 15 секунд вполне возможно, что в какую-то субботу ближе к концу октября наступит что-то вроде часа ночи, а системные часы вернутся на 1 час назад (в США и большей части По крайней мере, в Европе), поэтому будильник не срабатывает раньше, чем через 1 час 30 секунд после его установки.
Янник
2

Так я запрограммировал эту задачу в собственном проекте. в приведенном ниже коде я использую

AlarmManager.ELAPSED_REALTIME_WAKEUP

установить будильник на определенное время. переменная intentName используется в intentFilter для получения этого сигнала тревоги. потому что я запускаю много сигналов тревоги этого типа. когда я отменяю все будильники. Я использую метод отмены. дано внизу.

// для удержания сигналов тревоги и отмены при необходимости

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

где функция диапазона это:

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

функция отмены тревоги это.

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

Некоторые важные примечания при выборе того, какой будильник использовать : (для тех, кто уже прочитал голоса за)

Долина RTC_WAKEUP смерти - изменение времени:
если пользователь вручную изменил время на прошлое, будильник не сработает, а будущее вызовет немедленное срабатывание будильника, если оно пройдет мимо RTCотметки времени.
Не делайте используйте этот сигнал тревоги для выполнения каких-либо проверок на стороне клиента / важных задач, потому что он может выйти из строя.

WAKEUP Смысл (Зефир и выше)
В общем - не так много. Не будет выводить устройство из спящего режима, когда оно idleвключено doze, для этого alarmManager.setExactAndAllowWhileIdleили alarmManager.setAndAllowWhileIdle( Дремота и режим ожидания )

Нир Дуан
источник