Android Studio使用AlarmManager设置多组闹钟触发异常问题
问题根因
- 使用了
setInexactRepeating闹钟接口:Android 4.4及以上版本该接口默认会对闹钟触发时间做批量对齐来降低功耗,天生存在数分钟级的延迟,无法满足准时触发要求 - 通知ID固定:所有通知调用
managerCompat.notify(15, notificationCompat)时均使用相同ID,后触发的通知会直接覆盖未展示的前序通知,导致多个闹钟仅能看到最后一条 - PendingIntent Flag不兼容高版本系统:
PendingIntent.getBroadcast使用flag=0,未指定可变/不可变属性,Android 12及以上系统会直接拦截此类PendingIntent,导致闹钟无法触发 - 代码语法错误:广播接收器中
createNotificationChannel方法写在了onReceive方法内部,Java不支持方法嵌套,会直接编译失败 - 未适配高版本权限:Android 12及以上要求应用持有
SCHEDULE_EXACT_ALARM权限才能使用精准闹钟,Android 13还需要POST_NOTIFICATIONS权限才能弹出通知 - 时间戳未做边界处理:如果传入的
UhrzeitInMillis是当天已过的时间,不会自动跳转到第二天,导致闹钟不会触发
修正代码方案
1. 清单文件添加权限与广播注册
<!-- 唤醒锁权限,保证休眠时闹钟能唤醒设备 --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- Android 12+ 精准闹钟权限 --> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <!-- Android 13+ 通知权限 --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- 监听设备开机广播,重启后可重新设置闹钟 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- 静态注册广播接收器,避免APP被后台清理后无法接收闹钟广播 --> <receiver android:name=".Optionen_Alarm"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
2. 修改闹钟设置方法
建议给4个时间选择器分别分配固定的requestCode(0/1/2/3),不要用动态生成的时间戳,避免后续无法找到对应闹钟进行更新/取消操作。
/** * 设置精准闹钟 * @param requestCode 每个闹钟对应唯一固定值:0/1/2/3 */ public void SetAlarm(Context context, long uhrzeitInMillis, int requestCode) { // 边界处理:如果所选时间早于当前时间,自动加一天推迟到次日触发 if (uhrzeitInMillis < System.currentTimeMillis()) { uhrzeitInMillis += AlarmManager.INTERVAL_DAY; } Intent intent = new Intent(context, Optionen_Alarm.class); // 携带闹钟ID,后续用来区分不同闹钟的通知 intent.putExtra("alarm_id", requestCode); // 适配高版本PendingIntent flag要求 int flag = PendingIntent.FLAG_UPDATE_CURRENT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { flag |= PendingIntent.FLAG_IMMUTABLE; } PendingIntent pendingIntent = PendingIntent.getBroadcast( context.getApplicationContext(), requestCode, intent, flag); AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); // 用精准闹钟接口,保证休眠状态下也能准时触发 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, uhrzeitInMillis, pendingIntent); } else { alarmManager.setExact(AlarmManager.RTC_WAKEUP, uhrzeitInMillis, pendingIntent); } }
3. 修改广播接收器代码
public class Optionen_Alarm extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "myapp:mywakelocktag"); // 加10秒超时,避免异常情况下唤醒锁不释放导致耗电 wl.acquire(10 * 1000L); // 获取当前闹钟ID,作为通知ID避免互相覆盖 int alarmId = intent.getIntExtra("alarm_id", 0); createNotificationChannel(context); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "1"); builder.setContentTitle("定时提醒"); builder.setContentText("你设置的时间到了!"); builder.setSmallIcon(R.drawable.picture); builder.setColor(context.getResources().getColor(R.color.red)); builder.setVibrate(new long[]{0, 300, 300, 300}); builder.setLights(Color.WHITE, 1000, 5000); builder.setAutoCancel(true); builder.setPriority(NotificationCompat.PRIORITY_HIGH); builder.setStyle(new NotificationCompat.BigTextStyle().bigText("你设置的时间到了!")); Intent notifyIntent = new Intent(context, Activity.class); int flag = PendingIntent.FLAG_UPDATE_CURRENT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { flag |= PendingIntent.FLAG_IMMUTABLE; } PendingIntent pendingIntent = PendingIntent.getActivity(context, alarmId, notifyIntent, flag); builder.setContentIntent(pendingIntent); Notification notificationCompat = builder.build(); NotificationManagerCompat managerCompat = NotificationManagerCompat.from(context); // 用不同的alarmId作为通知ID,不会互相覆盖 managerCompat.notify(alarmId, notificationCompat); // 重新设置下一天的闹钟,实现每日重复 // 注意:需要提前将每个alarmId对应的时间毫秒值存储到SharedPreferences等持久化容器中 // long savedTime = 从持久化容器取出对应alarmId的时间戳; // SetAlarm(context, savedTime, alarmId); wl.release(); } // 方法移到onReceive外部,修复语法错误 private void createNotificationChannel(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { CharSequence name = "定时提醒"; String description = "每日定时提醒通知"; int importance = NotificationManager.IMPORTANCE_HIGH; NotificationChannel channel = new NotificationChannel("1", name, importance); channel.setDescription(description); channel.enableVibration(true); channel.setVibrationPattern(new long[]{0, 300, 300, 300}); NotificationManager notificationManager = context.getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); } } }
额外注意事项
- Android 12及以上需要在代码中动态申请
SCHEDULE_EXACT_ALARM权限,Android 13及以上需要动态申请POST_NOTIFICATIONS权限,无权限时闹钟和通知都无法正常工作 - 国产ROM(小米、华为、OPPO等)需要手动给APP开启自启动权限、后台弹出界面权限、允许通知权限,否则系统会拦截后台闹钟和通知
- 如果需要设备重启后闹钟依然生效,需要在收到
BOOT_COMPLETED广播时,重新设置所有已保存的闹钟
内容的提问来源于stack exchange,提问作者Nicklas_Mns
相关产品推荐
相关产品推荐

