如何使用AlarmManager实现每日/每周重复提醒?浇水闹钟不重复问题解决
浇水提醒仅触发一次无法重复的问题修复
核心问题分析
- PendingIntent标记错误:低版本系统中使用了
FLAG_ONE_SHOT,这个标记会让PendingIntent触发一次后立即失效,导致后续重复闹钟无法正常触发。 - 时间逻辑冲突:
scheduleAlarm方法中同时混用了Calendar设置星期几和datePlanted + timeTonotify解析时间的逻辑,可能导致目标时间计算错误。 - 高版本AlarmManager限制:Android 6.0+对后台闹钟有严格限制,
setRepeating在高版本系统中无法保证精准重复,需结合setExactAndAllowWhileIdle手动重新调度。
修复方案
1. 修正PendingIntent标记
将低版本中的FLAG_ONE_SHOT替换为FLAG_UPDATE_CURRENT,保证PendingIntent可以重复使用;高版本同时保留FLAG_IMMUTABLE以符合系统要求。
2. 统一时间计算逻辑
放弃混用两种时间设置方式,基于Calendar直接计算目标触发时间,并处理当前时间已过的情况。
3. 适配高版本重复闹钟
在广播接收器中收到闹钟触发后,手动重新调度下一次闹钟,避免依赖setRepeating的不可靠行为。
修改后的关键代码
调度闹钟方法scheduleAlarm
private void scheduleAlarm(String text, int dayOfWeek, long time, int intervalTime) { AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Calendar calendar = Calendar.getInstance(); // 设置目标时间的时分秒(假设time为时间选择器返回的毫秒数,对应时分) calendar.set(Calendar.HOUR_OF_DAY, (int) (time / 3600000)); calendar.set(Calendar.MINUTE, (int) ((time % 3600000) / 60000)); calendar.set(Calendar.SECOND, 0); // 每周重复时设置对应星期几 if (intervalTime == 7 * 24 * 60 * 60 * 1000) { calendar.set(Calendar.DAY_OF_WEEK, dayOfWeek); } // 若当前时间已过目标时间,往后推一个周期 if (calendar.getTimeInMillis() < System.currentTimeMillis()) { calendar.add(Calendar.MILLISECOND, intervalTime); } Intent intent = new Intent(getApplicationContext(), AlarmBroadCastForTrack.class); intent.putExtra("message", text); intent.putExtra("interval", intervalTime); // 传递周期,用于后续重新调度 // 构建可重复使用的PendingIntent PendingIntent pendingIntent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); } else { pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } // 适配不同版本的AlarmManager API if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent); } else { am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), intervalTime, pendingIntent); } Toast.makeText(this, "Alarm Setup Successful", Toast.LENGTH_SHORT).show(); }
广播接收器AlarmBroadCastForTrack
public class AlarmBroadCastForTrack extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras(); String text = bundle.getString("message"); int interval = bundle.getInt("interval"); // 构建并显示通知 Intent intent1 = new Intent(context, NotificationMessageForTrack.class); intent1.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent1.putExtra("message", text); PendingIntent pendingIntent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { pendingIntent = PendingIntent.getActivity(context, 1, intent1, PendingIntent.FLAG_IMMUTABLE); } else { pendingIntent = PendingIntent.getActivity(context, 1, intent1, PendingIntent.FLAG_UPDATE_CURRENT); } NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, "notify_001"); RemoteViews contentView = new RemoteViews(context.getPackageName(), R.layout.notification_layout_for_track); contentView.setImageViewResource(androidx.appcompat.R.id.image, R.mipmap.ic_launcher); contentView.setTextViewText(R.id.message, text); mBuilder.setSmallIcon(R.drawable.ic_baseline_alarm_on_24); mBuilder.setAutoCancel(true); mBuilder.setPriority(Notification.PRIORITY_HIGH); mBuilder.setContent(contentView); mBuilder.setContentIntent(pendingIntent); // 适配Android 8.0+通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String channelId = "channel_id"; if (notificationManager.getNotificationChannel(channelId) == null) { NotificationChannel channel = new NotificationChannel(channelId, "浇水提醒", NotificationManager.IMPORTANCE_HIGH); channel.enableVibration(true); notificationManager.createNotificationChannel(channel); } mBuilder.setChannelId(channelId); } Notification notification = mBuilder.build(); notificationManager.notify(1, notification); // 重新调度下一次闹钟 rescheduleAlarm(context, text, interval); } private void rescheduleAlarm(Context context, String text, int interval) { AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.MILLISECOND, interval); Intent intent = new Intent(context, AlarmBroadCastForTrack.class); intent.putExtra("message", text); intent.putExtra("interval", interval); PendingIntent pendingIntent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); } else { pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent); } else { am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), interval, pendingIntent); } } }
额外注意事项
- 确保应用已申请
RECEIVE_BOOT_COMPLETED权限,并在广播接收器中监听开机广播,重启后重新调度闹钟,避免设备重启后闹钟失效。 - 对于Android 12+,需在Manifest中声明
USE_EXACT_ALARM权限,确保精准闹钟可以正常触发。
内容的提问来源于stack exchange,提问作者Leeyan
相关产品推荐
相关产品推荐

