Alarm manager: repeating alarm not always fires
Asked Answered
F

2

0

I'm trying to develop a kind of reminders system, so user can enter days for reminders (MON, TUE...) and times on which this reminder will fire (only hour and minutes). It can be any day of the week or multiple days of the week. Here is the code for setting these reminders:

        final AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        for (final Day day: reminder.getDays()) {
            for (final ReminderTime reminderTime: reminder.getTimes()) {
                final Calendar calendar = Calendar.getInstance();
                calendar.set(Calendar.HOUR_OF_DAY, reminderTime.getHour());
                calendar.set(Calendar.MINUTE, reminderTime.getMinute());
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.DAY_OF_WEEK, day.getCalendarDay());

                if (calendar.getTimeInMillis() < System.currentTimeMillis()) {
                    // final long daysDifference = DateUtils.getDaysDifference(calendar.getTimeInMillis(), System.currentTimeMillis());
                    // calendar.add(Calendar.DAY_OF_YEAR, (int) (daysDifference + 1));
                    calendar.add(Calendar.DAY_OF_YEAR, 7);
                }

                final Intent intent = createReminderIntent(context, reminder.getReminderType(), reminderTime, day);
                final PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
                alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY * 7, sender);
            }
        }

The alarm I recieve later in custom BroadcastReceiver. The problem is that the reminder is firing from time to time and sometimes not, seems to me that something is woring with the days. I'm wondering were is the bug or what I'm doing wrong?

Fechter answered 19/5, 2015 at 18:47 Comment(7)
You set the same request code (zero) for all the pending intents. The last one replaces all who came before it. Using variable request code, should fix it. Read PendingIntent class overview to understand how this works.Subliminal
By variable request code you mean always new request code? I know how this PendingIntent works, however I don't understand why zero request code is not valid for such case? I thought the flag I'm setting in the last parameter defines if it will be replaced, updated or cancelled.Fechter
It is valid but you want to plan the same intent multiple times. Therefore you need to generate different pending intents for each one repeating cycle. One way to ensure that is using different request codes.Subliminal
Thank you for your response, I will try and let you know later if it will work.Fechter
One more question, for cancelling all these alarms, I will need the same request codes for PendingIntent which I will use here?Fechter
Afraid so, you'll have to keep references to the pending intents or the request codes so you can reconstruct them and then call AlarmManager.cancel(pi) for each of those. I don't know of any way around this.Subliminal
Thanks for your help, seems that was the problem. You can post your answer to the question so I can accept it as the best one.Fechter
S
1

When calling PendingIntent.get*(...) with the same Intent and request code the same instance of PendingIntent is returned.

The AlarmManager can have only one rule associated for one PendingIntent which means that only the last alarm set by AlarmManager.setRepeating(...) in your code is actually active. The others got overwritten by this last rule.

One way to differentiate PendingIntents with the same Intent is to use a different request code for each. When passed to AlarmManager these will trigger individual alarms as expected.

Sadly there's no way to cancel multiple alarms defined e.g. by base Intent so you have to either

  • keep all your PendingIntent instances used originally to schedule the alarms
  • keep all the request codes and reconstruct said PendingIntents
  • or use similar mechanism.

Then you need to call AlarmManager.cancel(pi) with each of these PendingIntents individually to cancel associated alarms.

Subliminal answered 19/5, 2015 at 20:45 Comment(0)
L
0
Calendar Calendar_Object = Calendar.getInstance();

    Calendar_Object.set(Calendar.DAY_OF_WEEK, 6);


    Calendar_Object.setTimeInMillis(System.currentTimeMillis());
    Calendar_Object.set(Calendar.HOUR_OF_DAY, 16);
    Calendar_Object.set(Calendar.MINUTE, 51);
    Calendar_Object.set(Calendar.SECOND, 0);

    // MyView is my current Activity, and AlarmReceiver is the
    // BoradCastReceiver
    Intent myIntent = new Intent(MyView.this, AlarmReceiver.class);

    PendingIntent pendingIntent = PendingIntent.getBroadcast(MyView.this,
            0, myIntent, 0);

    AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

    /*
     * The following sets the Alarm in the specific time by getting the long
     * value of the alarm date time which is in calendar object by calling
     * the getTimeInMillis(). Since Alarm supports only long value , we're
     * using this method.
     */

      Long alarmTime = Calendar_Object.getTimeInMillis();

    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, alarmTime, 7 * 24 * 60 * 60 * 1000 , pendingIntent);
Lathrop answered 19/5, 2015 at 18:54 Comment(2)
I've tried that, but it really doesn't change anything, the time for the calendar in milliseconds is correct.Fechter
I believe OP needs to plan multiple concurrent alarms using the same intent. How does your snippet achieve this?Subliminal

© 2022 - 2024 — McMap. All rights reserved.