Android Studio每日定时通知异常:仅打开App触发及重复推送问题求助
解决Android每日10点通知的触发问题
作为经常和AlarmManager打交道的开发者,我一眼就看出你的代码里几个典型的使用误区,帮你逐个拆解问题并给出落地的修复方案:
问题根源拆解
- 仅10点开APP才收到/首次安装就触发:你设置的Calendar目标时间是当天10点,但如果APP启动时已经过了10点,
cal.getTimeInMillis()会返回一个过去的时间戳。AlarmManager的逻辑是“逾期任务立刻执行”,所以一打开APP就会触发通知。 - 每次打开APP都收到通知:每次进入
MainActivity的onCreate都会调用sendNotification(),重复创建相同的PendingIntent并设置Alarm。由于PendingIntent已经存在,AlarmManager会更新这个任务,但只要当前时间仍在10点之后,依旧会立刻触发一次。
修复后的完整代码实现
1. 修正MainActivity的sendNotification方法
核心改动:
- 确保触发时间是未来的10点(如果当前已过10点,自动切换到次日)
- 添加PendingIntent存在性检查,避免重复设置Alarm
- 使用适配高版本Android的PendingIntent Flag
public void sendNotification() { Calendar cal = Calendar.getInstance(); // 先把时间锚定到今日10:00整 cal.set(Calendar.HOUR_OF_DAY, 10); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); // 如果当前时间已经过了10点,自动把目标时间改成明天的10点 if (System.currentTimeMillis() > cal.getTimeInMillis()) { cal.add(Calendar.DAY_OF_YEAR, 1); } AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(this, NotificationReceiver.class); // 先检查这个PendingIntent是否已经存在,避免重复设置Alarm PendingIntent existingIntent = PendingIntent.getBroadcast( this, 0, intent, // 适配API 31+的Flag,只查询不创建 PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE ); // 只有当PendingIntent不存在时,才初始化并设置Alarm if (existingIntent == null) { PendingIntent pendingIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // 不同API版本用不同的Alarm设置方式,保证触发精确性 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // API23+用这个,即使APP在待机也能触发 alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent ); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // API19+用精确触发 alarmManager.setExact( AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent ); } else { // 低版本直接用重复触发 alarmManager.setRepeating( AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent ); } } }
2. 优化NotificationReceiver的逻辑
注意:setExactAndAllowWhileIdle和setExact不会自动重复触发,所以需要在Receiver里手动设置次日的Alarm,同时保留你的周五不通知逻辑:
public class NotificationReceiver extends BroadcastReceiver { private static final String NOTIFICATION_CHANNEL_ID = "001"; @Override public void onReceive(Context context, Intent intent) { Calendar calendar = Calendar.getInstance(); // 周五跳过通知 if (calendar.get(Calendar.DAY_OF_WEEK) != Calendar.FRIDAY) { // 构建通知内容 Intent mainIntent = new Intent(context, MainActivity.class); PendingIntent pIntent = PendingIntent.getActivity( context, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel notificationChannel = new NotificationChannel( NOTIFICATION_CHANNEL_ID, "每日操作提醒", NotificationManager.IMPORTANCE_DEFAULT ); notificationChannel.setDescription("每日10点提醒完成指定操作"); notificationChannel.enableLights(true); notificationChannel.setLightColor(Color.RED); notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000}); notificationChannel.enableVibration(true); notificationManager.createNotificationChannel(notificationChannel); } NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID); notificationBuilder.setAutoCancel(true) .setDefaults(Notification.DEFAULT_ALL) .setWhen(System.currentTimeMillis()) .setSmallIcon(R.drawable.dropdown) .setTicker("提醒来了") .setContentTitle("每日操作提醒") .setContentText("该完成今日的特定操作啦!") .addAction(R.mipmap.back, "现在处理", pIntent) .setContentInfo("提醒"); notificationManager.notify(1, notificationBuilder.build()); } // 手动设置次日的Alarm,保证每天都能触发 resetNextDayAlarm(context); } private void resetNextDayAlarm(Context context) { Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, 10); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); // 直接设置为次日10点 cal.add(Calendar.DAY_OF_YEAR, 1); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, NotificationReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast( context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent ); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alarmManager.setExact( AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent ); } else { alarmManager.setRepeating( AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent ); } } }
3. 额外注意事项
- 从API19开始,
setInexactRepeating会批量触发任务,无法保证精确到10点,所以推荐用setExactAndAllowWhileIdle配合手动重复的方案。 - API31+要求PendingIntent必须设置
FLAG_IMMUTABLE或FLAG_MUTABLE,否则会抛出异常。 - 如果APP被用户强行杀死,Alarm可能会被清除,你可以添加
BOOT_COMPLETED广播监听,在设备重启后重新设置Alarm(需要添加权限<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />并注册对应的Receiver)。
内容的提问来源于stack exchange,提问作者Hozayfa El Rifai
相关产品推荐
相关产品推荐

