华为、三星手机应用关闭后Alarm Manager无法正常触发问题
问题修复方案
现有代码存在多处API使用、配置逻辑错误,加上华为、三星定制ROM的后台管控机制,共同导致应用退后台/被杀死后闹钟无法触发,按以下步骤修复即可:
1. 修正AlarmManager调用逻辑
你当前的闹钟API选择、参数配置存在多个兼容性问题:
- 不要用
AlarmManager.RTC类型,该类型不会唤醒休眠中的设备,应用退后台后极易错过触发时间,必须替换为AlarmManager.RTC_WAKEUP - 普通
set()方法在Android 6.0以上的Doze模式下会被系统批量推迟触发,无法保证准点,需要分版本使用高优先级闹钟API - PendingIntent不能传flag=0,Android 12+必须指定可变性flag,否则闹钟设置直接失败
- 原有逻辑顺序错误:先设置闹钟再判断是否需要取消,存在竞态风险,应该先判断开关状态,再执行设置操作
修复后的闹钟设置代码:
private fun setAlarm(timeInMillis: Long) { val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager val intent = Intent(this, MyAlarmReceiver::class.java) // 适配不同版本PendingIntent flag要求 val piFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT } else { PendingIntent.FLAG_UPDATE_CURRENT } val pendingIntent = PendingIntent.getBroadcast(this, 0, intent, piFlags) // 先取消已存在的同ID闹钟,避免重复触发 alarmManager.cancel(pendingIntent) // 闹钟开关判断,不需要开启直接返回 if ((actual_alarm_on_off =="OFF" && actual_alarm_on_off != "ON1") || (alarmReleased == true && actual_alarm_on_off != "ON1")){ return } // 分版本使用可靠的唤醒闹钟API when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> { // 支持Doze模式下唤醒设备触发,每个应用每9分钟最多调用1次,符合系统限制 alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent ) } Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> { alarmManager.setExact( AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent ) } else -> { alarmManager.set( AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent ) } } }
2. 补全清单文件权限与组件配置
你当前缺少必要权限、广播接收器配置不全,应用被杀、设备重启后闹钟会直接失效:
- 必须声明唤醒锁、通知、精准闹钟、开机监听权限
- 广播接收器需要显式声明enabled属性,添加开机广播监听:设备重启后AlarmManager设置的所有闹钟会被清空,必须收到开机广播后重新设置闹钟
- 包名加"alarm"关键词对新系统完全无效,反而可能触发厂商ROM的异常管控,建议去掉
修改后的AndroidManifest.xml相关配置:
<!-- 必要权限声明 --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android12+ 精准闹钟权限 --> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <!-- 开机广播权限,用于重启后恢复闹钟 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <receiver android:name="com.example.medbesteller_alarm.MyAlarmReceiver" android:enabled="true" android:exported="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /> </intent-filter> </receiver>
同时需要在MyAlarmReceiver的onReceive中增加开机广播处理逻辑:收到开机广播后,读取本地存储的预设闹钟时间,重新调用setAlarm方法恢复闹钟。
3. 修复通知相关代码问题
你当前的通知渠道创建存在资源引用错误,Android13+缺少动态权限申请:
- 通知渠道名不能直接传
"@string/app"字符串,这不会解析资源,会导致渠道名显示异常 - Android13及以上版本必须动态申请
POST_NOTIFICATIONS权限,否则通知无法弹出 - 华为、三星ROM对后台通知管控严格,可以在闹钟触发时启动一个优先级更高的前台服务来保证提醒正常展示,避免被系统拦截
修复后的通知渠道创建代码:
private fun createNotificationChannel(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationChannel = NotificationChannel( NOTIFICATION_CHANNEL_ID, context.getString(R.string.app), // 正确引用字符串资源 NotificationManager.IMPORTANCE_HIGH ).apply { enableVibration(true) // 按需配置通知铃声、震动模式 } NotificationManagerCompat.from(context).createNotificationChannel(notificationChannel) } }
4. 厂商ROM适配注意事项
华为、三星的自定义ROM对后台应用的管控比原生Android更严格,仅修改系统通用通知、电池设置不够:
- 需要引导用户在系统自带的手机管家中,手动给应用开启「允许自启动」「允许后台活动」「无限制电池策略」,这部分管控逻辑是厂商自定义的,和原生Android的电池优化白名单不是同一套配置
- 不要设置间隔小于9分钟的重复闹钟,会被系统直接拦截
- 测试时不要通过最近任务列表划掉应用的方式验证,华为、三星ROM会在用户划掉应用时强制停止进程、取消所有关联闹钟,属于厂商自定义行为,需要提示用户不要手动划掉应用,或者加入厂商的后台白名单
内容的提问来源于stack exchange,提问作者Thomas Kupfer
相关产品推荐
相关产品推荐

