API 26升级后AlarmManager在Android 6.0+后台无法正常工作
解决API 26+下AlarmManager后台失效的问题
我太懂你这种升级API后踩坑的感受了——把项目升到API 26之后,AlarmManager在Android 6.0+设备后台完全失效,还试过WakefulBroadcastReceiver没用,这确实让人头大,毕竟你需要的是每15分钟稳定触发的重复闹钟。
为什么原来的方法失效了?
- API 26+的后台执行限制:Android从API 26开始收紧了后台应用的权限,普通的
AlarmManager.setRepeating()在应用进入后台后会被系统延迟甚至直接忽略,尤其是Doze模式和App Standby机制会抑制非必要的后台操作。 - WakefulBroadcastReceiver已废弃:这个组件在API 26中被官方标记为废弃,取而代之的是更适配新后台规则的WorkManager或结合前台服务的精确闹钟方案。
解决方案一:高精度重复闹钟(适合需严格15分钟间隔的场景)
如果你的业务要求闹钟必须精准触发,不能有较大延迟,推荐使用AlarmManager.setExactAndAllowWhileIdle(),并通过单次闹钟+重复设置的方式实现周期触发(因为API 23+后重复闹钟的精确性无法保证)。
1. 创建广播接收器处理闹钟触发逻辑
public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 这里写闹钟触发后要执行的逻辑 // 注意:不要在这里做耗时操作,否则会触发ANR // 立即设置下一次15分钟后的闹钟 scheduleNextAlarm(context); } private void scheduleNextAlarm(Context context) { AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, AlarmReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast( context, 0, // 请求码,可根据需要修改 intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // 计算15分钟后的触发时间 long nextTriggerTime = System.currentTimeMillis() + 15 * 60 * 1000; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // API 23+使用精确且允许Doze模式下触发的方法 alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, // 唤醒设备触发 nextTriggerTime, pendingIntent ); } else { // 低版本使用setExact保证精确性 alarmManager.setExact( AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent ); } } }
2. 初始化第一次闹钟
在你的Activity或启动类中调用以下代码,设置第一个15分钟后的闹钟:
private void initPeriodicAlarm() { AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(this, AlarmReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); long firstTriggerTime = System.currentTimeMillis() + 15 * 60 * 1000; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, firstTriggerTime, pendingIntent ); } else { alarmManager.setExact( AlarmManager.RTC_WAKEUP, firstTriggerTime, pendingIntent ); } }
3. 注意事项
如果你的闹钟触发后需要执行耗时操作,绝对不能在BroadcastReceiver中处理,必须启动一个前台服务(API 26+后台服务启动会被限制),前台服务需要显示一个通知告知用户。
解决方案二:WorkManager(适合允许一定延迟的场景)
如果你的业务可以接受15分钟左右的误差(系统可能在10-15分钟之间触发),WorkManager是更省心的选择,它会自动适配系统的后台限制,无需手动处理Doze模式等问题。
1. 创建Worker类
public class PeriodicTaskWorker extends Worker { public PeriodicTaskWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { // 这里写闹钟触发后要执行的逻辑 return Result.success(); // 任务执行成功 } }
2. 启动周期性任务
在合适的地方(比如Application的onCreate或Activity的onCreate)调用以下代码:
// 创建周期性工作请求,最小间隔为15分钟,允许5分钟的弹性时间 PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder( PeriodicTaskWorker.class, 15, TimeUnit.MINUTES, 5, TimeUnit.MINUTES ).build(); // 将任务加入WorkManager队列 WorkManager.getInstance(getApplicationContext()).enqueue(periodicWork);
总结
- 若需要高精度的15分钟重复触发:选择
AlarmManager.setExactAndAllowWhileIdle()+广播接收器+前台服务(如需耗时操作)的方案。 - 若允许一定延迟:直接用WorkManager,它会帮你处理所有后台适配问题。
内容的提问来源于stack exchange,提问作者Uday Ramjiyani
相关产品推荐
相关产品推荐

