如何让AlarmManager的setExactAndAllowWhileIdle实现重复触发?
好问题!确实从Android 6.0的Doze模式开始,普通的重复闹钟会被系统限制,而setExactAndAllowWhileIdle虽然能突破Doze限制,但只能触发一次。不过我们可以通过每次触发完成后重新设置下一次闹钟的方式,模拟出“重复触发”的效果,完美适配低功耗空闲模式。
下面给你具体的实现方案:
核心思路
放弃setRepeating,改用setExactAndAllowWhileIdle设置单次闹钟,然后在广播接收器的onReceive方法里,处理完当前业务后,立即计算下一次触发时间并重新设置闹钟,以此实现循环触发。
步骤1:修改初始闹钟设置代码
把你原来的setRepeating替换成setExactAndAllowWhileIdle,适配不同Android版本:
// 获取当前时间并设置第一次触发时间(1分钟后) Calendar cal2 = Calendar.getInstance(); cal2.add(Calendar.MINUTE, 1); Intent intentTracking = new Intent(getApplicationContext(), TrackingAlarmReceiver.class); // 注意Android 12+需要添加FLAG_IMMUTABLE,避免PendingIntent异常 PendingIntent sender3 = PendingIntent.getBroadcast( getApplicationContext(), 192839, intentTracking, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); AlarmManager am3 = (AlarmManager) getSystemService(ALARM_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // Android 6.0及以上,使用能突破Doze的setExactAndAllowWhileIdle am3.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal2.getTimeInMillis(), sender3); } else { // 低版本设备可以直接用setRepeating或者setExact am3.setRepeating(AlarmManager.RTC_WAKEUP, cal2.getTimeInMillis(), Integer.parseInt(carerTrackingInteval) * 60000, sender3); }
步骤2:修改广播接收器TrackingAlarmReceiver
在接收器里完成业务逻辑后,立即设置下一次闹钟:
public class TrackingAlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 1. 先执行你的核心业务逻辑(比如追踪任务) executeTrackingTask(); // 2. 计算下一次触发的时间(这里用你定义的间隔时间) long intervalMillis = Integer.parseInt(carerTrackingInteval) * 60000; long nextTriggerTime = System.currentTimeMillis() + intervalMillis; // 3. 重新设置下一次的闹钟 Intent nextIntent = new Intent(context, TrackingAlarmReceiver.class); PendingIntent nextSender = PendingIntent.getBroadcast( context, 192839, nextIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, nextSender); } else { am.setExact(AlarmManager.RTC_WAKEUP, nextTriggerTime, nextSender); } } private void executeTrackingTask() { // 这里写你的追踪业务代码,注意不要做耗时操作 // 如果任务耗时,建议启动前台服务处理,避免被系统杀死 } }
关键注意事项
- Doze模式的频率限制:即使使用
setExactAndAllowWhileIdle,系统在Doze模式下也会限制这类闹钟的触发频率(通常每15分钟最多一次,不同版本有差异),这是系统的功耗保护机制,无法完全绕过。如果需要更高频的触发,建议考虑WorkManager的PeriodicWorkRequest,它会尽量在系统维护窗口执行任务。 - 开机重启恢复:记得申请
RECEIVE_BOOT_COMPLETED权限,并在开机广播接收器里重新初始化闹钟,避免设备重启后闹钟丢失。 - 耗时任务处理:广播接收器的生命周期很短,不要在
onReceive里执行耗时操作,复杂任务建议启动前台服务完成。
内容的提问来源于stack exchange,提问作者turtleboy
相关产品推荐
相关产品推荐

