Android AlarmManager应用关闭/被杀/设备休眠时闹钟不触发问题
AlarmManager后台/进程被杀后闹钟不触发修复
现有代码存在几个核心适配问题,按以下步骤修改即可覆盖应用后台、休眠、进程被杀场景:
- PendingIntent未配置正确的可变性Flag,安卓12及以上版本系统会直接拦截未声明可变性的PendingIntent,导致广播无法触发
- 缺少安卓13及以上版本必需的通知运行时权限,通知无法正常弹出
- 未处理系统电池优化限制,安卓原生及国产定制ROM默认会对后台应用做进程冻结、杀死处理,AlarmManager触发后无法拉起应用组件
- 未监听设备开机广播,设备重启、进程被强制杀死后,之前注册的闹钟任务会被系统清空,不会自动恢复
- 未提前校验精确闹钟权限,安卓12及以上版本用户可以手动关闭应用的精确闹钟调度权限,关闭后
setExact类接口会失效
1. 补全Manifest配置
在原有配置基础上补充缺失的权限、开机广播接收器:
<!-- 原有权限保留 --> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 新增以下权限 --> <!-- 安卓13+通知运行时权限 --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- 开机广播权限,设备重启后恢复闹钟 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- 电池优化白名单权限 --> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <!-- 原有AlarmReceiver保留 --> <receiver android:name=".newAlarmBroadcast.AlarmReceiver" android:enabled="true" android:exported="false" /> <!-- 新增开机广播接收器 --> <receiver android:name=".newAlarmBroadcast.BootReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /> </intent-filter> </receiver>
2. 修正所有PendingIntent的Flag配置
替换所有PendingIntent获取逻辑的Flag参数,适配安卓12+的可变性要求:
首先修改闹钟设置逻辑:
@SuppressLint("SimpleDateFormat") private void setTheAlarm(long dateTime) { alarmManager = (AlarmManager) requireActivity().getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(requireActivity(), AlarmReceiver.class); // 配置正确的PendingIntent Flag int pendingFlag = PendingIntent.FLAG_UPDATE_CURRENT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { pendingFlag |= PendingIntent.FLAG_IMMUTABLE; } PendingIntent pendingIntent = PendingIntent.getBroadcast(requireActivity(), Constants.ALARM_ID, intent, pendingFlag); // 安卓12+先校验精确闹钟权限,未授权则引导用户开启 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (!alarmManager.canScheduleExactAlarms()) { startActivity(new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)); return; } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, dateTime, pendingIntent); } else { alarmManager.setExact(AlarmManager.RTC_WAKEUP, dateTime, pendingIntent); } }
再修改Notification类中跳转用的PendingIntent:
// 替换原有PendingIntent获取逻辑 int pendingFlag = PendingIntent.FLAG_UPDATE_CURRENT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { pendingFlag |= PendingIntent.FLAG_IMMUTABLE; } PendingIntent pendingIntent = PendingIntent.getActivity(context, id, intent, pendingFlag);
3. 处理运行时权限与电池优化
- 在页面启动时动态申请安卓13+的通知权限:调用
ActivityCompat.requestPermissions申请Manifest.permission.POST_NOTIFICATIONS,用户授权后才能正常弹出通知 - 引导用户将应用加入电池优化白名单,避免系统杀死后台进程:
// 检查是否已加入电池优化白名单 @RequiresApi(api = Build.VERSION_CODES.M) private boolean isInBatteryWhiteList() { PowerManager pm = (PowerManager) requireContext().getSystemService(Context.POWER_SERVICE); return pm.isIgnoringBatteryOptimizations(requireContext().getPackageName()); } // 跳转申请页面 @RequiresApi(api = Build.VERSION_CODES.M) private void requestBatteryWhiteList() { try { Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + requireContext().getPackageName())); startActivity(intent); } catch (Exception e) { // 部分定制ROM无专属申请页,跳转到应用详情页让用户手动开启自启、后台运行权限 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + requireContext().getPackageName())); startActivity(intent); } }
4. 实现开机广播,恢复闹钟任务
设备重启、进程被强制清理后,之前注册的Alarm任务会被系统清除,需要在开机完成后重新注册所有闹钟:
public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_BOOT_COMPLETED.equals(action) || Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) { // 从本地存储(Room、SP等)读取所有已设置的闹钟时间,循环调用setTheAlarm方法重新注册即可 } } }
额外注意事项
- 广播接收器
onReceive方法只有10秒左右的执行窗口,如果需要实现响铃、持续震动等长耗时逻辑,不要直接在Receiver里写,要启动前台服务处理,避免进程被系统直接掐断 - 部分国产定制ROM(小米、华为、OPPO等)对
setExactAndAllowWhileIdle接口有调用配额限制,默认单个应用每天仅能触发几次,必须引导用户在系统设置中手动给应用开启自启、后台无限制权限才能正常触发 - 不要在广播接收器里直接启动Activity,安卓10及以上版本限制了后台启动Activity的行为,通过通知的PendingIntent跳转即可正常唤起页面
内容的提问来源于stack exchange,提问作者Block Person
相关产品推荐
相关产品推荐

