Android AlarmManager - RTC_WAKEUP kontra ELAPSED_REALTIME_WAKEUP

87

Czy ktoś może mi wyjaśnić różnicę między AlarmManager.RTC_WAKEUPiAlarmManager.ELAPSED_REALTIME_WAKEUP ? Przeczytałem dokumentację, ale nadal nie do końca rozumiem konsekwencje używania jednego nad drugim.

Przykładowy kod:

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

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

Jak różne będą dwie linie kodu wykonane? Kiedy te dwa wiersze kodu zostaną wykonane względem siebie?

Doceniam twoją pomoc.

Camille Sévigny
źródło

Odpowiedzi:

140

AlarmManager.ELAPSED_REALTIME_WAKEUP typ służy do wyzwalania alarmu od czasu uruchomienia:

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

spowoduje, że alarm włączy się 10 minut po uruchomieniu urządzenia .

Istnieje licznik czasu, który uruchamia się po uruchomieniu urządzenia, aby zmierzyć czas pracy urządzenia i jest to typ, który uruchamia alarm w zależności od czasu pracy urządzenia.

Natomiast AlarmManager.RTC_WAKEUPuruchomi alarm zgodnie z czasem zegara. Na przykład, jeśli:

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

to z drugiej strony uruchomi alarm za 30 sekund .

AlarmManager.ELAPSED_REALTIME_WAKEUPtyp jest rzadko używany w porównaniu z AlarmManager.RTC_WAKEUP.

Rejinderi
źródło
1
Tak myślałem. Potrzebowałem tylko potwierdzenia. Więc jeśli zrobiłem coś takiego: alarmManager.set (AlarmManager.ELAPSED_REALTIME_WAKEUP, System.currentTimeMills () + 30 * 1000, pendingIntent); mogą się zdarzyć dziwne rzeczy (co miałem, dopóki nie zauważyłem, że nie działa tak, jak myślałem.
Camille Sévigny
2
Pamiętaj, że kod powinien być System.currentTimeMillis()zamiast System.currentTimeMills():)
HasanAboShally
17
„Typ AlarmManager.ELAPSED_REALTIME_WAKEUP jest rzadko używany w porównaniu do AlarmManager.RTC_WAKEUP.” To są spekulacje i zła rada. Zgodnie z dokumentacją: developer.android.com/training/scheduling/alarms.html „Jeśli chcesz, aby po prostu alarm uruchamiał się w określonych odstępach czasu (na przykład co pół godziny), użyj jednego z typów czasu rzeczywistego, które upłynęły. Ogólnie jest to lepszy wybór ”.
Jared Kells
1
Odpowiedź @ mborsuk jest lepsza i poprawiając tę ​​odpowiedź: Nie powinieneś używać RTC przez upływający czas, np thirtySecondsFromNow.
Micha F.
2
Całkiem dobre wyjaśnienie :)! Chciałbym dodać ważny komentarz: w odniesieniu do oficjalnych dokumentów z Androida użycie „AlarmManager.ELAPSED_REALTIME_WAKEUP” może być czymś interesującym, jeśli chodzi o aplikację, która odpala żądania HTTP do serwera i nie chcesz generować żadnych załaduj na swoim serwerze internetowym. Pomyśl o tysiącach urządzeń z Androidem wykonujących operację GET na serwerach WWW o 22:30: ze względu na to, że działa w czasie rozruchu urządzenia, „AlarmManager.ELAPSED_REALTIME_WAKEUP” może sprawić, że te „tysiące” urządzeń będą uruchamiać żądania, a nie w tym samym czasie, unikając obciążenia :).
ivanleoncz
107

Pomimo obecnie zaakceptowanej i przegłosowanej odpowiedzi, typy AlarmManager.ELAPSED_REALTIME * wraz z SystemClock.elapsedRealtime () zawsze były bardziej niezawodne niż zegary RTC dla alarmów i czasu.

Używanie ELAPSED_REALTIME_WAKEUP z AlarmManager będzie polegać na zegarze monotonicznym począwszy od czasu rozruchu i będzie kontynuował tykanie nawet wtedy, gdy procesor jest w trybach oszczędzania energii, więc jest to zalecana podstawa dla ogólnego pomiaru czasu w odstępach czasu . Więc,

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

sprawi, że Twój PendingIntent zostanie uruchomiony w ciągu 1 minuty (60 * 1000 milisekund).

Podczas gdy AlarmManager.RTC_WAKEUP jest dla standardowego czasu "ściany" w milisekundach od epoki. Więc,

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

może również wywołać alarm za 60 sekund od teraz, ale nie niezawodnie, ponieważ jak zaznaczono w dokumentacji SystemClock :

Zegar ścienny może być ustawiany przez użytkownika lub sieć telefoniczną (patrz setCurrentTimeMillis (long)), dzięki czemu czas może przeskakiwać do tyłu lub do przodu w nieprzewidywalny sposób. Tego zegara należy używać tylko wtedy, gdy ważna jest zgodność z rzeczywistymi datami i godzinami, na przykład w aplikacji kalendarza lub budzika. Pomiary interwałów lub czasu, który upłynął, powinny wykorzystywać inny zegar. Jeśli używasz System.currentTimeMillis (), rozważ wysłuchanie transmisji ACTION_TIME_TICK, ACTION_TIME_CHANGED i ACTION_TIMEZONE_CHANGED, aby dowiedzieć się, kiedy zmienia się czas.

Ponadto pytanie odnosiło się tylko do alarmów * _WAKEUP, ale zapoznaj się również z dokumentacją AlarmManager , aby upewnić się, że rozumiesz, co zapewniają alarmy budzenia, a co nie.

mborsuk
źródło
Twoja odpowiedź jest świetna, ale w mojej aplikacji musiałem ustawić alarmy, które pokrywały się z rzeczywistą datą / godziną, a nie tylko 60 sekund od teraz. W takim przypadku RTC_WAKEUP jest dla mnie najlepszym rozwiązaniem.
Camille Sévigny
8
W porządku, ale jest to dokładniejsza odpowiedź na pytanie i poprawia rzeczy w aktualnie zaakceptowanej odpowiedzi.
mborsuk
+1 dla tych informacji, ale pamiętaj, że elapsedRealtime()jest pod SystemClockzamiast System. EDYCJA: ... Osiągnięto dzienny limit głosów ...
Jorge Fuentes González
To jedna z najlepszych odpowiedzi w tym numerze. Szukałem igły w stogu siana o wysokości 50 m (aka „błąd w moim kodzie!”), A Twoja odpowiedź zaprowadziła mnie prosto do igły!
AlxDroidDev
17

Tylko uwaga. Możesz uzyskać czas pracy w milisach połączeń:

long uptimeMillis =  SystemClock.elapsedRealtime();

Więc jeśli chcesz uruchomić alarm za 30 sekund od teraz i chcesz użyć zegara uptime zamiast normalnego zegara, możesz wykonać:

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

Ilekroć chcesz sprawdzić upływ czasu zamiast określonej daty / godziny, najlepiej jest skorzystać z uptime. Dzieje się tak, ponieważ aktualny czas ustawiony przez użytkownika w urządzeniu może ulec zmianie, jeśli użytkownik zmieni go za pomocą ustawień.

Włącz
źródło
3
+1 za wskazanie, że należy użyć tego „zawsze, gdy chcesz sprawdzić, jaki upłynął czas”. Idealnie ma sens.
sulai
Znowu tylko dla podkreślenia: rozumiem, że to na pewno uruchomi się za 30 sekund od teraz, chyba że stanie się coś takiego jak wyłączenie urządzenia. Konkretnie, myślę, że problem z używaniem RTC_WAKEUP polega na tym, że teoretycznie za 15 sekund od teraz jest możliwe, że stanie się to około 1 w nocy w jakąś sobotę pod koniec października, a zegar systemowy cofnie się o 1 godzinę (w USA i większość Przynajmniej Europa), więc alarm nie uruchamia się wcześniej niż 1 godzinę i 30 sekund po ustawieniu.
Yannick
2

W ten sposób zaprogramowałem ten problem we własnym projekcie. w poniższym kodzie używam

AlarmManager.ELAPSED_REALTIME_WAKEUP

ustawić alarm na określony czas. zmienna „intentName” jest używana w intentFilter do odbierania tego alarmu. ponieważ odpalam wiele alarmów tego typu. kiedy anuluję wszystkie alarmy. używam metody anuluj. podane na dole.

// do wstrzymywania alarmów i anulowania ich w razie potrzeby

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

gdzie funkcja span jest taka:

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

to jest funkcja kasowania alarmu.

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
źródło
1

Kilka ważnych uwag przy wyborze alarmu do użycia : (dla kogo już przeczytano głosy za)

RTC_WAKEUP Death Valley - zmiana czasu:
Jeśli użytkownik musi ręcznie czas przesiąść się na przeszłość alarmu nie zgaśnie, a przyszłość spowoduje alarm zgaśnie natychmiast piłkę do RTCdatownik.
Nie rób używaj tego alarmu do weryfikacji po stronie klienta / ważnych zadań, ponieważ może się nie powieść.

WAKEUP Sens (Zefir i powyżej)
w ogóle - nie dużo. Nie wybudza urządzenia w trakcie idlelub w trakcie doze, w tym celu alarmManager.setExactAndAllowWhileIdlelub alarmManager.setAndAllowWhileIdle( Drzemka i bezczynność )

Nir Duan
źródło