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

AlarmManager设置同一时间时onReceive仅触发一次的问题排查

解决相同时间AlarmManager仅触发一次的问题

嗨,我完全懂你遇到的这个坑——明明给每个闹钟用了不同的requestCode创建PendingIntent,相同时间的闹钟却只触发一次,这确实让人困惑!

问题根源

你用的setInexactRepeating()是Android为了省电设计的API,它的核心特性就是会把时间接近的多个inexact闹钟合并触发,哪怕你的PendingIntent是不同的,系统也会为了优化电池使用,把它们归为同一批处理,只触发一次。这就是为什么两个相同时间的闹钟只收到一次通知的原因。

解决方案

根据你的需求,有两种可行的解决方式:

方案1:改用精确闹钟+手动重复(推荐)

放弃系统的setInexactRepeating(),改用精确闹钟API,然后在每次触发通知后,手动设置下一天的闹钟。这样每个闹钟都会被独立触发,不会被合并。

修改设置闹钟的代码:

for(int i = 0; i < dosageDtoList.size();i++) { 
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 
    DosageDto dosageDto = dosageDtoList.get(i); 
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); 
    String time = dosageDto.getReminderTime(); 
    String dateTime = startDateStr + " " + time; 
    Date formattedDate = simpleDateFormat.parse(dateTime); 
    Calendar cal = Calendar.getInstance(); 
    cal.setTime(formattedDate);

    // 避免设置过去的闹钟,如果当前时间已过,直接设置第二天的
    if (cal.getTimeInMillis() < System.currentTimeMillis()) {
        cal.add(Calendar.DAY_OF_YEAR, 1);
    }

    Intent notificationIntent = new Intent("android.media.action.DISPLAY_NOTIFICATION"); 
    notificationIntent.addCategory("android.intent.category.DEFAULT"); 
    notificationIntent.putExtra(Constants.POS,i); 
    notificationIntent.putExtra(Constants.END_DATE, endDateStr); 
    notificationIntent.putExtra(Constants.TITLE, userDrugDto.getUserName()); 
    notificationIntent.putExtra(Constants.SUB_TITLE, userDrugDto.getDrugName()); 
    notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); 

    // 适配API31+的PendingIntent flags,优先用IMMUTABLE保证安全
    int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S 
        ? PendingIntent.FLAG_IMMUTABLE 
        : PendingIntent.FLAG_UPDATE_CURRENT;
    PendingIntent broadcast = PendingIntent.getBroadcast(context, i, notificationIntent, flags); 

    if(alarmManager != null) { 
        // 根据API版本选择合适的精确闹钟API
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // 允许在设备休眠时触发,适合提醒类场景
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), broadcast);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), broadcast);
        } else {
            alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), broadcast);
        }
    } 
}

然后在AlarmReceiver中,触发通知后手动设置下一天的闹钟:

public class AlarmReceiver extends BroadcastReceiver { 
    @Override 
    public void onReceive(Context context, Intent intent) { 
        // 1. 生成并显示你的通知(这里保留你原来的代码)

        // 2. 获取当前闹钟的参数,准备设置下一次闹钟
        int pos = intent.getIntExtra(Constants.POS, -1);
        String endDateStr = intent.getStringExtra(Constants.END_DATE);
        String title = intent.getStringExtra(Constants.TITLE);
        String subTitle = intent.getStringExtra(Constants.SUB_TITLE);

        // 3. 从存储中获取对应pos的DosageDto(比如用SharedPreferences或Room数据库)
        // 这里假设你已经实现了数据存储逻辑,能拿到对应的dosageDto
        DosageDto dosageDto = getDosageDtoByPos(pos); 
        String time = dosageDto.getReminderTime();
        
        try {
            // 计算下一天的触发时间
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            
            Calendar nextCal = Calendar.getInstance();
            nextCal.add(Calendar.DAY_OF_YEAR, 1);
            String nextDateStr = dateFormat.format(nextCal.getTime());
            String nextDateTime = nextDateStr + " " + time;
            
            Date nextTriggerDate = dateTimeFormat.parse(nextDateTime);
            Calendar triggerCal = Calendar.getInstance();
            triggerCal.setTime(nextTriggerDate);

            // 检查是否超过结束日期,超过则停止重复
            Date endDate = dateFormat.parse(endDateStr);
            if (triggerCal.getTime().after(endDate)) {
                return;
            }

            // 4. 创建下一次的Intent和PendingIntent
            Intent nextIntent = new Intent("android.media.action.DISPLAY_NOTIFICATION");
            nextIntent.addCategory("android.intent.category.DEFAULT");
            nextIntent.putExtra(Constants.POS, pos);
            nextIntent.putExtra(Constants.END_DATE, endDateStr);
            nextIntent.putExtra(Constants.TITLE, title);
            nextIntent.putExtra(Constants.SUB_TITLE, subTitle);
            nextIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);

            int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S 
                ? PendingIntent.FLAG_IMMUTABLE 
                : PendingIntent.FLAG_UPDATE_CURRENT;
            PendingIntent nextBroadcast = PendingIntent.getBroadcast(context, pos, nextIntent, flags);

            // 5. 设置下一次的精确闹钟
            AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
            if (alarmManager != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerCal.getTimeInMillis(), nextBroadcast);
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerCal.getTimeInMillis(), nextBroadcast);
                } else {
                    alarmManager.set(AlarmManager.RTC_WAKEUP, triggerCal.getTimeInMillis(), nextBroadcast);
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
    } 

    // 这里需要你实现的方法:根据pos从存储中获取对应的DosageDto
    private DosageDto getDosageDtoByPos(int pos) {
        // 示例:从SharedPreferences或数据库读取数据
        return ...;
    }
}

方案2:给相同时间的闹钟加微小时间偏移

如果你坚持要用setInexactRepeating(),可以给每个相同时间的闹钟添加几秒钟的偏移,让系统认为它们是不同时间的闹钟,避免被合并。比如在设置cal的时候加上:

// 给每个闹钟加i*1000毫秒的偏移,确保时间不重叠
cal.add(Calendar.MILLISECOND, i * 1000);

这种方式简单,但缺点是闹钟的触发时间会有微小偏差,适合对时间精度要求不高的场景。

额外注意事项

  • API版本适配:不同Android版本的AlarmManager API有差异,一定要根据targetSdk版本选择合适的方法,避免兼容性问题。
  • PendingIntent Flags:API 31+建议使用FLAG_IMMUTABLE,避免潜在的安全风险;如果需要修改Intent的extra,再用FLAG_UPDATE_CURRENT。
  • 数据持久化:方案1中需要把闹钟的配置信息(比如DosageDto、结束日期等)保存下来,这样在Receiver里才能重新设置下一次的闹钟,推荐用Room数据库或者SharedPreferences存储。

内容的提问来源于stack exchange,提问作者Thankless

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 09:07:24