You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio每日定时通知异常:仅打开App触发及重复推送问题求助

解决Android每日10点通知的触发问题

作为经常和AlarmManager打交道的开发者,我一眼就看出你的代码里几个典型的使用误区,帮你逐个拆解问题并给出落地的修复方案:

问题根源拆解

  1. 仅10点开APP才收到/首次安装就触发:你设置的Calendar目标时间是当天10点,但如果APP启动时已经过了10点,cal.getTimeInMillis()会返回一个过去的时间戳。AlarmManager的逻辑是“逾期任务立刻执行”,所以一打开APP就会触发通知。
  2. 每次打开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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:04:16