【问题标题】:Alarm Notification fires instantly. Android警报通知立即触发。安卓
【发布时间】:2026-01-22 20:25:01
【问题描述】:

我正在开发一个在固定时间向用户发送通知的提醒。

闹钟马上响起……

我尝试了* 的大部分建议,但仍然遇到同样的问题

请帮我解决这个问题。

服务器数据

user_reminder": [
                {
                    "id": "75",
                    "name": "Morning Snacks",
                    "time": "11:00:00",
                    "days": "1,2,3,4,5,6,7",
                    "user_id": "14"
                },
                {
                    "id": "76",
                    "name": "Lunch",
                    "time": "13:00:00",
                    "days": "1,2,3,4,5,6,7",
                    "user_id": "14"
                },
               ......
            ]

我的代码

for (int i = 0; i < reminderList.size(); i++) 
{
     String time = reminderList.get(i).getTime(); // "time": "11:00:00"

    String strSpit[] = time.split(":");
    String strDays[] = reminderList.get(i).getDays().split(","); //"days": "1,2,3,4,5,6,7"

    Date date = new Date();
    Calendar calNow = Calendar.getInstance();
    calNow.setTime(date);

    Calendar calAlarm = Calendar.getInstance();
    calAlarm.set(Calendar.HOUR_OF_DAY, Integer.parseInt(strSpit[0]));
    calAlarm.set(Calendar.MINUTE, Integer.parseInt(strSpit[1]));

    for (int j = 0; j < strDays.length; j++) 
    {
        calAlarm.set(Calendar.DAY_OF_WEEK, viewFunctions.getDayInt(strDays[j]));

        if (calAlarm.before(calNow)) 
        {
            //if its in the past increment
            calAlarm.add(Calendar.DATE, 1);
        }

        notifyIntent.putExtra(Constants.REMINDER_NAME, reminderList.get(i).getName());
        pendingIntent = PendingIntent.getBroadcast(this, 0, notifyIntent, PendingIntent.FLAG_ONE_SHOT);
        alarmManager.set(AlarmManager.RTC_WAKEUP, calAlarm.getTimeInMillis() , pendingIntent);

        }
    }
}

获取天数:这解决了天数编号

public int getDayInt(String strDay) 
{
   int dayNumber = 0;

   if (strDay.equals("1")) 
   {
       dayNumber = Calendar.MONDAY;

   } ......

   return dayNumber;
}

屏幕截图

【问题讨论】:

  • 一个问题:你知道Calendar.DAY_OF_WEEK这个字段以星期日开头,值为1吗?那这个呢:calAlarm.add(Calendar.DATE, 1);?如果这一天不仅仅是昨天,如果是 3 天前呢?那么它仍然是过去......
  • 是的.. viewFunctions.getDayInt() 解决了这个问题......
  • 解决哪一个? DAY_OF_WEEK 还是三天前的那件事?
  • 仅解决 DAY_OF_WEEK ...请查看更新后的代码..
  • 问题仍未解决:(

标签: android notifications alarmmanager


【解决方案1】:

问题

您的闹钟会立即响起,因为 Android 会触发过去安排的所有闹钟。

由于以下代码未按预期工作,您的某些警报在过去已被安排。您问题的示例代码:

if (calAlarm.before(calNow)) 
{
   //if [it's] in the past increment
   calAlarm.add(Calendar.DATE, 1);
}

在上面的代码中,如果闹钟是过去的,您只会在闹钟中添加一天。因此,假设您在星期五运行此代码,并且您在星期一阅读了警报。您的代码将在星期二的日期上增加一天,安排该警报。警报已过去,因为星期二仍然在星期五之前,因此 Android 将在安排后不久触发该警报。

更新

您的问题不清楚您希望如何处理过去的提醒。一种可能的解决方案是将它们安排在未来 1 周。

if(calAlarm.before(calNow)) 
{
   // If it's in the past increment by one week.
   calAlarm.add(Calendar.DATE, 7);
}

【讨论】:

  • 我已经知道这种情况..现在正在寻找解决方案。
  • 我明白了,我不清楚你是否知道罪魁祸首是什么。这完全取决于您想对这些信息做什么。您想安排当前日期 + 1 天吗?定义需求很重要。
  • 我的要求是简单的提醒。我不希望我过去的提醒被触发。您直接将它们安排在未来 1 周后,此用户可能会错过提醒。
  • 这有帮助吗... long diff = calNow.getTime().getTime() - calAlarm.getTime().getTime(); int 天 = (int) (diff / (1000*60*60*24)); if (days==1){ calAlarm.add(Calendar.DATE, 1); } else if (days==2){ calAlarm.add(Calendar.DATE, 2); }
  • 有助于将您希望代码执行的操作转化为文字。您想如何处理过去的日期?
【解决方案2】:

主要问题似乎出在这一行:

calAlarm.set(Calendar.DAY_OF_WEEK, viewFunctions.getDayInt(strDays[j]));

您需要意识到的是,这只是设置将在输出中显示的星期几 - 它不会更改要匹配的基础日期,我认为这是您所期望的。

尝试使用以下代码更改日期,以便为所选的每一天设置闹钟:

String strSpit[] = time.split(":");
String strDays[] = reminderList.get(i).getDays().split(","); //"days": "1,2,3,4,5,6,7"

Calendar todayWithTime = Calendar.getInstance(); //setting current time is redundant
todayWithTime.set(Calendar.HOUR_OF_DAY, Integer.parseInt(strSpit[0]));
todayWithTime.set(Calendar.MINUTE, Integer.parseInt(strSpit[1]));

Calendar alarm;
int today = todayWithTime.get(Calendar.DAY_OF_WEEK);
int offset, target;

for (int j = 0; j < strDays.length; j++) {

    alarm = (Calendar) todayWithTime.clone(); //now you have todays date, but correct time
    target = strDays[j];
    //saturday is biggest day of week
    offset  = (Calendar.SATURDAY - today + target) % 7; //work out how many days in the future the next occurance of this day is
    alarm.add(Calendar.DATE, offset);

    ... // the rest stays the same

}

【讨论】:

  • 现在没有警报响起。
  • 你调试了吗?在 alarm.add 打印出格式化的日期时间之后,您可以准确地看到它的计划时间
  • 偏移量是负数吗?也许它需要 Math.abs();
  • 请看所附的屏幕截图......它是印度星期五下午 2:15 的时间表,已通过......通过警报的偏移量为 0
  • 这意味着什么日子(target 是什么)+ 你期待什么?
【解决方案3】:

我之前也有同样的问题,请检查以下详细信息:

不工作的代码示例:

Intent notificationIntent = new Intent("~~~.BaseActivity");
        notificationIntent.putExtra("type", 2);
        notificationIntent.putExtra("appName", "testApp");
        notificationIntent.putExtra("messageEN", "Good evening");
        notificationIntent.putExtra("notificaitonID", 4);


        PendingIntent broadcast = PendingIntent.getBroadcast(context, 4,
                notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 18);
        calendar.set(Calendar.MINUTE, 10);
        calendar.set(Calendar.SECOND, 0);
        // this is to show it at the 6:10 

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);



        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
                    calendar.getTimeInMillis(),
                    AlarmManager.INTERVAL_DAY,
                    broadcast);

工作代码:

    Intent notificationIntent = new Intent("~~~.BaseActivity");
    notificationIntent.putExtra("type", 2);
    notificationIntent.putExtra("appName", "testApp");
    notificationIntent.putExtra("messageEN", "Good evening");
    notificationIntent.putExtra("notificaitonID", 4);


    PendingIntent broadcast = PendingIntent.getBroadcast(context, 4,
            notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.HOUR_OF_DAY, 18);
    calendar.set(Calendar.MINUTE, 10);
    calendar.set(Calendar.SECOND, 0);
    // this is to show it at the 6:10 

    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);


    Calendar nowCalendar = Calendar.getInstance();

    if (calendar.after(nowCalendar)) {
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
                calendar.getTimeInMillis(),
                AlarmManager.INTERVAL_DAY,
                broadcast);

    } else {
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
                calendar.getTimeInMillis(),
                AlarmManager.INTERVAL_DAY,
                broadcast);
    }

只有当你要设置重复时才完成,你需要检查它是否通过,如果通过了只需添加想要的重复时间

【讨论】:

  • 这里的问题是这并没有解决这个特定的问题。它为每一天设置一个间隔,不管选择了什么,如果过去就增加一天,即使它本可以在 5 天前过去 并且 如果在他的循环中使用,最终会产生多达 7 个警报每天安排
【解决方案4】:

在寻找解决方案时遇到了同样的问题并偶然发现了这个问题。 设置闹钟时,您只需勾选闹钟日期不应早于当前日期。

public static void setReminder(Context context,Class<?> cls,long milliseconds, int event_id,String eventName)
{
    Calendar calendar = Calendar.getInstance();

    Calendar notificationcalendar = Calendar.getInstance();

    notificationcalendar.setTimeInMillis(milliseconds);

    if(!notificationcalendar.before(calendar)) { // **just add this check**


        ComponentName receiver = new ComponentName(context, cls);
        PackageManager pm = context.getPackageManager();

        pm.setComponentEnabledSetting(receiver,
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);


        Intent intent1 = new Intent(context, cls);
        intent1.putExtra("eventName", eventName);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, event_id, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
        AlarmManager am = (AlarmManager) context.getSystemService(ALARM_SERVICE);
        am.setInexactRepeating(AlarmManager.RTC_WAKEUP, notificationcalendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
    }

}

【讨论】:

    【解决方案5】:

    最后我找到了一种方法,将PendingIntent requestCode 存储在数据库中(使用ROOM),然后通过从DB 检索所有requestCode 来取消所有警报

    AlarmIdPojo

    @Entity
    public class AlarmIdPojo {
    
        @PrimaryKey(autoGenerate = true)
        public int id;
    
        private int requestCode;
    
        public AlarmIdPojo() {
        }
    
        public int getRequestCode() {
            return requestCode;
        }
    
        public void setRequestCode(int requestCode) {
            this.requestCode = requestCode;
        }
    }
    

    AlarmIdDAO

    @Dao
    public interface AlarmIdDAO {
    
        @Query("select * from AlarmIdPojo")
        List<AlarmIdPojo> getAllRequestCode();
    
        @Query("delete from AlarmIdPojo")
        public void deleteAllRequestCode();
    
        @Insert(onConflict = REPLACE)
        void addRequestCode(AlarmIdPojo pojo);
    }
    

    应用数据库

    @Database(entities = {AlarmIdPojo.class}, version = 1)
    public abstract class AppDatabase extends RoomDatabase {
    
        public abstract AlarmIdDAO requestIdPojo();
    
        @Override
        protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config) {
            return null;
        }
    
        @Override
        protected InvalidationTracker createInvalidationTracker() {
            return null;
        }
    }
    

    来电提醒

    private void callReminder() {
    
    
            //  java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
            // because of this Exception , we are doing this in AsyncTask
    
            new AsyncTask<Void, Void, Void>() {
                @Override
                protected Void doInBackground(Void... voids) {
                    List<AlarmIdPojo> idList = appDatabase.requestIdPojo().getAllRequestCode();
    
                    Intent notifyIntent = new Intent(MainActivity.this, MyReceiver.class);
                    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
                    PendingIntent pendingIntent;
    
                    for (int i = 0; i < idList.size(); i++) {
    
    
                        int requestId = idList.get(i).getRequestCode();
    
                        pendingIntent = PendingIntent.getBroadcast(MainActivity.this, requestId, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
                        // Cancel alarms
                        try {
                            alarmManager.cancel(pendingIntent);
                        } catch (Exception e) {
                            Log.e(TAG, "AlarmManager update was not canceled. " + e.toString());
                        }
    
                    }
    
                    appDatabase.requestIdPojo().deleteAllRequestCode();
                    return null;
                }
    
                @Override
                protected void onPostExecute(Void aVoid) {
                    super.onPostExecute(aVoid);
    
                    // Once every request code is deleted , then once again call setReminderNotification() for fresh data.
                    setReminderNotification();
    
                }
            }.execute();
    
    
        }
    

    setReminderNotification

    private void setReminderNotification() {
    
            Intent notifyIntent = new Intent(this, MyReceiver.class);
            AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
            PendingIntent pendingIntent;
    
    
            // Taking existing offline reminder data from sharePreference
            Type type = new TypeToken<List<UserReminderPojo>>() {
            }.getType();
            List<UserReminderPojo> reminderList = new Gson().fromJson(sharedPrefUtils.getString(sharedPrefUtils.DEFAULT_REMINDERS), type);
    
    
            for (int i = 0; i < reminderList.size(); i++) {
    
                String time = reminderList.get(i).getTime();
    
                String strSpit[] = time.split(":");
    
                String strDays[] = reminderList.get(i).getDays().split(",");
    
    
                Calendar todayWithTime = Calendar.getInstance();
                todayWithTime.set(Calendar.SECOND, 0);
                todayWithTime.set(Calendar.MILLISECOND, 0);
    
    
                for (int j = 0; j < strDays.length; j++) {
    
                    Calendar alarm = Calendar.getInstance();
                    alarm.set(Calendar.SECOND, 0);
                    alarm.set(Calendar.MILLISECOND, 0);
    
                    alarm.set(Calendar.HOUR_OF_DAY, Integer.parseInt(strSpit[0]));
                    alarm.set(Calendar.MINUTE, Integer.parseInt(strSpit[1]));
                    alarm.set(Calendar.DAY_OF_WEEK, viewFunctions.getDayInt(strDays[j]));
    
    
                    int randomPendingIntentId = generateRandomId();
                    notifyIntent.putExtra(Constants.REMINDER_NAME, reminderList.get(i).getName());
                    notifyIntent.putExtra(Constants.ID, randomPendingIntentId); // passing it , so that we can cancel this PendingIntent with this Id, once notification is shown.This is done to prevent past time alarm firing
                    notifyIntent.putExtra(Constants.REMINDER_DAY, viewFunctions.getDayInt(strDays[j]));
                    pendingIntent = PendingIntent.getBroadcast(this, randomPendingIntentId, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
                    if (alarm.before(todayWithTime)) {
                        alarm.add(Calendar.DATE, 7);
                    }
    
                    alarmManager.set(AlarmManager.RTC_WAKEUP, alarm.getTimeInMillis(), pendingIntent);
    
                    insertToDB(randomPendingIntentId);
    
                }
            }
    
        }
    

    insertToDB

    // Saving to DB. keeping track  of PendingIntent unique id.
        private void insertToDB(int randomPendingIntentId) {
            alarmIdPojo = new AlarmIdPojo();
            alarmIdPojo.setRequestCode(randomPendingIntentId);
    
            //  java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
            // because of this Exception , we are doing this in AsyncTask
    
            new AsyncTask<Void, Void, Void>() {
                @Override
                protected Void doInBackground(Void... voids) {
                    appDatabase.requestIdPojo().addRequestCode(alarmIdPojo);
                    return null;
                }
            }.execute();
    
        }
    

    【讨论】: