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

这两行代码的执行方式有何不同?这两行代码何时才能相对执行?

我感谢您的帮助。

Answers:


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_WAKEUP与相比,很少使用type AlarmManager.RTC_WAKEUP


1
那正是我所想。我只需要一些确认。因此,如果我做了这样的事情:alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,System.currentTimeMills()+ 30 * 1000,pendingIntent);可能会发生奇怪的事情(这是我所拥有的,直到我注意到它没有按我的想法工作。)
CamilleSévigny

2
请注意,该代码应System.currentTimeMillis()代替System.currentTimeMills():)
HasanAboShally

17
“与AlarmManager.RTC_WAKEUP相比,很少使用AlarmManager.ELAPSED_REALTIME_WAKEUP类型。” 这是猜测和不好的建议。根据文档: developer.android.com/training/scheduling/alarms.html “如果您只需要按特定间隔(例如,每半小时)触发警报,请使用一种经过的实时类型。一般而言,这是更好的选择。”
杰瑞德·凯尔斯

1
@mborsuk的答案更好,并且可以更正此答案:不应将RTC用于已用时间,例如for thirtySecondsFromNow
Micha F.

2
很好的解释:)!我想添加一条重要的评论:关于Android的正式文档,当涉及到向服务器触发HTTP请求的应用程序时,使用“ AlarmManager.ELAPSED_REALTIME_WAKEUP”可能会很有趣,并且您不想生成任何内容在您的网络服务器上加载。考虑一下在晚上10:30在Web服务器上执行GET的数千个Android设备:由于在设备的启动时间内可以正常工作,“ AlarmManager.ELAPSED_REALTIME_WAKEUP”可能会使这些“数千个”设备触发请求,而不是在同一时间,避免负荷:)。
ivanleoncz '16

107

尽管当前已接受并投票否决了答案,但AlarmManager.ELAPSED_REALTIME *类型以及SystemClock.elapsedRealtime()始终比RTC时钟在警报和计时方面更可靠。

与AlarmManager一起使用ELAPSED_REALTIME_WAKEUP将依赖于从启动时间开始的单调时钟,即使CPU处于省电模式,该时钟仍将继续计时,因此通用间隔定时的推荐依据也是“”。所以,

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

将在1分钟(60 * 1000毫秒)内触发PendingIntent。

而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是我的最佳解决方案。
卡米尔·塞维尼(CamilleSévigny),2013年

8
很好,但这是对问题的更准确答案,并且可以更正当前接受的答案中的内容。
mborsuk

此信息+1,但请注意,该信息elapsedRealtime()位于下方,SystemClock而不是System。编辑:...达到每日投票限制...
豪尔赫·富恩特斯·冈萨雷斯

这是那里问题的最佳答案之一。我在一个高50m的大海捞针中寻找针头(又名“我的代码中的错误!”),您的回答使我直奔针头!
AlxDroidDev

17

请注意。您可以得到正常运行时间的millis呼叫:

long uptimeMillis =  SystemClock.elapsedRealtime();

因此,如果您要从现在起30秒后发出警报,并且想要使用正常运行时间时钟而不是正常时钟,则可以执行以下操作:

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

每当您要检查经过的时间而不是特定的日期/时间时,最好使用正常运行时间。这是因为,如果用户使用设置对其进行更改,则用户在设备中设置的当前时间可能会更改。


3
+1指出要使用“无论何时要检查经过的时间”。完全有道理。
sulai 2012年

再次强调一下:我的理解是,除非设备关机等事情发生,否则肯定会从现在起30秒内触发。具体来说,我认为使用RTC_WAKEUP的问题是,从理论上讲,从现在起15秒钟,可能是在10月底附近某个星期六的凌晨1点左右,系统时钟返回1个小时(在美国,至少在欧洲),因此,闹钟在设置后1小时30秒才真正触发。
Yannick

2

我以此方式在自己的项目中编写了此问题。在下面的代码中我正在使用

AlarmManager.ELAPSED_REALTIME_WAKEUP

在特定时间设置闹钟。intentFilter中使用变量'intentName'接收此警报。因为我会发出许多此类警报。当我取消所有警报时。我使用取消方法。在底部给出。

//保留警报并在需要时取消

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

其中span函数是这样的:

 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.setExactAndAllowWhileIdlealarmManager.setAndAllowWhileIdle打盹&空闲

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.