Search code examples
androidandroid-pendingintentandroid-alarms

Alarms not cancelled


I'm using the following code to cancel all my alarms and reset them:

    for (int i = 0; i < mArrayList.size(); i++) {
  Intent receiverIntent = new Intent(mContext, AlarmReceiver.class);
        int _id = (int) mArrayList.get(i).getDateMillis(); //(int) System.currentTimeMillis();
        PendingIntent sender = PendingIntent.getBroadcast(mContext, _id, receiverIntent, PendingIntent.FLAG_CANCEL_CURRENT);
        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(mContext.ALARM_SERVICE);
        alarmManager.cancel(sender);
        setAlarm(mContext, receiverIntent, mArrayList.get(i).getType(), _id, sender, alarmManager);
           }

    public static void setAlarm(Context context, Intent receiverIntent, String typ, long timeMillis, PendingIntent pendingIntent, AlarmManager 
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(timeMillis);
        long sdl = calendar.getTimeInMillis();

        String notificationTitle = "My Title";
        String notificationText = "My Text";
        receiverIntent.putExtra("notificationTitle", notificationTitle);
        receiverIntent.putExtra("notificationText", notificationText);
        receiverIntent.putExtra("notificationDateTime", sdl);

        alarmManager.set(AlarmManager.RTC_WAKEUP, sdl, pendingIntent);
    }

Problem is that when I first call CancelAllAlarms() and then set my alarms the new alarms are created very good but the old ones are not deleted. And so if I do a adb shell dumpsys alarm > D:\test.txt the first time I have about 200 entries, the next the the app is run I have about 400, then 600 etc...

EDIT

I changed my code a lot because I'm now storing my generated ID's inside a DB which works fine. So my code has changed quiet significant:

Intent receiverIntent = new Intent(mContext, AlarmReceiver.class); AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(mContext.ALARM_SERVICE);

    String[] alarms = getAllAlarmsFromDB(databaseHAlarms, databaseHAlarms.tableName_alarms);
    for (int i = 0; i < alarms.length; i++) {
        //alarms[i] now holds the ID set before (see codee below)
        PendingIntent sender = PendingIntent.getBroadcast(mContext, Integer.parseInt(alarms[i]), receiverIntent, PendingIntent.FLAG_CANCEL_CURRENT);
        alarmManager.cancel(sender);
    }

    for (int i = 0; i < mArrayList.size(); i++) {
        long timeMillis = mArrayList.get(i).getDateMillis();
        int _id = Integer.parseInt(String.valueOf(mArrayList.get(i).getDateMillis()).substring(0, 8));
        PendingIntent sender = PendingIntent.getBroadcast(mContext, _id, receiverIntent, PendingIntent.FLAG_CANCEL_CURRENT);

        insertAlarmIntoMySQL(databaseHAlarms, getMD5(timeMillis + ""), String.valueOf(mArrayList.get(i).getDateMillis()).substring(0, 8));

        alarmManager.set(AlarmManager.RTC_WAKEUP, sdl, sender);
    }

Solution

  • In addition to user13s answer, you have to use FLAG_UPDATE_CURRENT on the PendingIntent.

    So in summary, be sure to use same Contextand id and use FLAG_UPDATE_CURRENT:

    start the alarm:

    PendingIntent sender = PendingIntent.getBroadcast(mContext, id, receiverIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    alarmManager.set(AlarmManager.RTC_WAKEUP, time, sender);
    

    stop the alarm:

    PendingIntent sender = PendingIntent.getBroadcast(mContext, id, receiverIntent, PendingIntent.FLAG_CUPDATE_CURRENT);
    alarmManager.cancel(sender);
    

    Why

    Well, it´s not exactly clear/documented why FLAG_CANCEL_CURRENT is not working, but I have read this often here in SO. The docs tell us, that this cancels an existing PendingIntent and creates a new one and you can use this if you want to change some extra data inside the intent.

    FLAG_UPDATE_CURRENT is described like:

    This can be used if you are creating intents where only the extras change, and don't care that any entities that received your previous PendingIntent will be able to launch it with your new extras even if they are not explicitly given to it.

    From these two description I can´t see a reason why it shouldn´t work with alarmManager.cancel() if you use FLAG_CANCEL_CURRENT . Maybe it´s because that the PendingIntentis canceled and recreated through the FLAG before alarmManager.cancel() is able to.

    Marshmallow

    For marshmallow there are some new circumstances. Cancel should work like before, there is no change. But setting an alarm with alarmManager.set()will not work if device falls into doze mode. The docs describing that you have to use setAndAllowWhileIdle(), setExactAndAllowWhileIdle() or setAlarmClock().

    BUT ONLY DO THIS IF YOUR APP NEEDS TO FIRE ALARMS ON DOZE! So to prepare your app for Marshmallow, you should check the API versions and set the method it depends on.

    if (Build.VERSION.SDK_INT >= 23) {   
    
      PendingIntent sender = PendingIntent.getBroadcast(mContext, id, receiverIntent, PendingIntent.FLAG_UPDATE_CURRENT);
       alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, sender);
    
    
    } else {        
    
        PendingIntent sender = PendingIntent.getBroadcast(mContext, id, receiverIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        alarmManager.set(AlarmManager.RTC_WAKEUP, time, sender);
    }