AlarmManager运行异常:定时推送通知延迟到达或完全不触发
问题根因分析
- 使用了
setInexactRepeating()接口,该接口从Android 4.4版本开始会被系统自动做触发时间对齐处理以降低功耗,本身就不保证精确触发,误差范围可达数分钟,且系统预设的最小间隔常量为15分钟,20分钟的自定义短间隔不符合系统对齐规则时会直接被跳过不触发。 - 广播接收器逻辑不全,当前仅处理了
BOOT_COMPLETED开机广播,闹钟正常触发时发送的自定义广播没有对应处理逻辑,闹钟触发时接收器不会执行通知推送逻辑。 PendingIntent的FLAG参数使用了0,从Android 12开始未明确指定FLAG_IMMUTABLE或FLAG_MUTABLE的PendingIntent会直接抛出异常,FLAG设置错误也会导致Intent携带的参数读取异常。- 未适配高版本Android后台限制,Android 6.0及以上的Doze模式、省电模式会延迟非白名单应用的闹钟触发,甚至直接拦截触发请求。
修复方案
1. 替换闹钟触发API
要求精确触发的场景不要使用系统提供的重复闹钟接口,改用单次精确闹钟,触发后手动设置下一次闹钟实现重复效果,适配不同系统版本的API要求:
private void setAlarm() { AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(this, AlertReceiver.class); // 自定义Action区分闹钟触发和开机广播 intent.setAction("com.yourapp.ALARM_TRIGGER"); intent.putExtra("supplementsId", supplements.getSupplementsId()); // 适配Android 12+的PendingIntent FLAG要求 int flag = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE : PendingIntent.FLAG_UPDATE_CURRENT; PendingIntent pendingIntent = PendingIntent.getBroadcast(this, requestCode++, intent, flag); Calendar c = Calendar.getInstance(); c.set(Calendar.HOUR_OF_DAY, hour); c.set(Calendar.MINUTE, minute); c.set(Calendar.SECOND, 0); // 避免设置的时间已过当前时间导致立即触发 if (c.getTimeInMillis() < System.currentTimeMillis()) { c.add(Calendar.DAY_OF_MONTH, 1); } // 选择对应版本的精确闹钟API if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pendingIntent); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alarmManager.setExact(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pendingIntent); } else { alarmManager.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pendingIntent); } }
2. 修复广播接收器逻辑
在onReceive方法中增加自定义闹钟触发Action的处理,触发后如果是重复需求可重新设置下一次闹钟:
@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // 同时处理开机广播和闹钟触发广播 if (TextUtils.equals(action, "android.intent.action.BOOT_COMPLETED") || TextUtils.equals(action, "com.yourapp.ALARM_TRIGGER")) { id = intent.getLongExtra("supplementsId", -1); this.context = context; getSupplements(); setNotification(); // 重复闹钟场景:此处补充逻辑重新调用setAlarm()设置下一次触发,短间隔直接加对应毫秒数即可 } }
3. 补充权限和清单配置
在AndroidManifest.xml中增加必要权限和接收器配置:
<!-- 接收开机广播权限 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- Android 12+精确闹钟权限 --> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <!-- 可选:申请加入省电白名单权限,提升闹钟触发率 --> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <!-- 注册广播接收器 --> <receiver android:name=".AlertReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="com.yourapp.ALARM_TRIGGER" /> </intent-filter> </receiver>
4. 短间隔提醒适配
如果需要小于15分钟的超短间隔提醒,可引导用户将应用加入系统省电白名单,或配合WorkManager做双保险机制,避免被系统后台拦截。
内容的提问来源于stack exchange,提问作者Byeongsu KIM
相关产品推荐
相关产品推荐

