3

I have 2 BroadcastReceivers and 2 intents, I want to click a button, 5m later start broadcast1 and 10m later start broadcast2, what's happening is they both start 10m after I click, My guess is, the intents are not unique, but I'm setting a diffrent reqeustCode for each of them.

Button's OnClick:

    Bundle bd = new Bundle();
    bd.putInt("mInt", i);

    Intent intent1 = new Intent(getActivity(), Broadcast_1.class);
    intent1.putExtras(bd);
    PendingIntent pendingIntent1 = PendingIntent.getBroadcast(getActivity().getApplicationContext(), i, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
    AlarmManager alarmManager1 = (AlarmManager) getActivity().getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    alarmManager1.setRepeating(AlarmManager.RTC, System.currentTimeMillis()+1000*60*5, 1000*60*10, pendingIntent1);
    Toast.makeText(getActivity(), "countdown started "+i ,Toast.LENGTH_SHORT).show();

    Intent intent2 = new Intent(getActivity(), Broadcast_1.class);
    intent2.putExtras(bd);
    PendingIntent pendingIntent2 = PendingIntent.getBroadcast(getActivity().getApplicationContext(), i+42212342, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
    AlarmManager alarmManager2 = (AlarmManager) getActivity().getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    alarmManager2.setRepeating(AlarmManager.RTC, System.currentTimeMillis()+1000*60*10, 1000*60*10, pendingIntent2);

BroadcastReceiver_1 and _2 (they look the same) class:

public class Broadcast_1 extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        Calendar c = Calendar.getInstance();
        int seconds = c.get(Calendar.SECOND);
        int minutes = c.get(Calendar.MINUTE);
        ShowTextFragment.setText("Broadcast_1" + " at " + minutes + " : " + seconds);

    }

}

Question: Why does the most recent intent push the previous intents to his starting time?

I confirmed this behaviour by printing the time when the Broadcast's code executes. Please help

John Sardinha
  • 3,079
  • 4
  • 21
  • 50

3 Answers3

2

The problem you are seeing is the way that repeating alarms now work. In order to preserve battery life, the AlarmManager now takes great liberties in rescheduling alarms in order to group multiple alarms together. Basically, if you need any sort of accurate timing you should forget about using setRepeating(). Use setExact() instead. If you need a repeating alarm, just reset it when it goes off.

David Wasser
  • 85,616
  • 15
  • 182
  • 239
  • How close must the alarms be, for android to group them? I am testing this with a short time but I actually want to use it with daily or weekly intervals – John Sardinha Jun 07 '16 at 13:30
  • I don't know and you shouldn't rely on it if you need anything close to exact timing. This is all vendor-specific so you may find that the behaviour/timing is different on a Samsung device than it is on an HTC or LG device (for example). – David Wasser Jun 07 '16 at 13:32
  • Well but I don't want to be draining the user's abttery unecessarily, there will be as much alarms as events the user add, so it may be a lot, do you have any slight idea of how much time it is for them to group? – John Sardinha Jun 07 '16 at 13:42
  • No, and I wouldn't worry about it. The user isn't going to add so many alarms that it will drain the battery. This just isn't anything for you to worry about. The changes in the `AlarmManager` are causing havoc for lots and lots of applications, it is a disaster :-( – David Wasser Jun 07 '16 at 15:24
  • Well, you've already seen one example where Android moved an alarm by 5 minutes in order to group it with another. Since none of this is documented you cannot rely on it being any specific number. – David Wasser Jun 07 '16 at 15:26
  • Yes you're right, so there is a `setExact()` option but I want them to be repeating and there is no `setExactRepeating()`, should I just use `setExact()` and call the broadcastReceiver again inside the broadcastReceiver itself? or is that kind of bad practice? – John Sardinha Jun 07 '16 at 15:28
  • Yes, that's exactly what is recommended. You schedule the alarm once using `setExact()`. When it goes off, you can schedule it again in `onReceive()`. This gives you complete control of when and how you reschedule the alarm. – David Wasser Jun 07 '16 at 15:38
  • Okay and what about before API 19? – John Sardinha Jun 07 '16 at 22:53
  • If the device is running Android older than 4.4 (API 19), `setRepeating()` works correctly on all devices (as far as I know). If the device is running Android 5.0 or higher, the behaviour is device-dependent. On some devices it works, on some it does not. `setExact()` is only available starting with API 19, so if you need to support older devices you will need to use `set()` on those devices instead of `setExact()`. – David Wasser Jun 08 '16 at 06:59
  • Okay and what's the current situation for `set()`, does it perform properly ? – John Sardinha Jun 08 '16 at 11:50
  • I have not done any exhaustive testing. My understanding is that set() works correctly if your targetSDK < API 19 and that it can also be inaccurate starting from API 19 (in which case you can use setExact() instead). – David Wasser Jun 08 '16 at 18:42
  • Alright, one thing, if I set an Alarm to start on DAY_OF_WEEK, Saturday, will it trigger immediately because of last saturday, or will it only trigger in the next saturday? – John Sardinha Jun 08 '16 at 18:57
  • You can't set an alarm to trigger on DAY_OF_WEEK. When you set an alarm you pass it a time parameter in milliseconds. This parameter is interpreted as either: wall-clock-time (if you specify `RTC` or `RTC_WAKEUP`) or as time-since-boot (if you specify `ELAPSED_REALTIME` of `ELAPSED_REALTIME_WAKEUP`. – David Wasser Jun 08 '16 at 19:35
  • If you pass a milliseconds value that you get from a `Calendar` where you set `DAY_OF_WEEK` = SATURDAY the answer is "it depends on what day of the week it is when you set it to SATURDAY". See http://stackoverflow.com/questions/1319473/java-calendar-setcalendar-day-of-week-calendar-sunday-will-it-roll-backwards – David Wasser Jun 08 '16 at 19:35
0

The reason for this is, that the intents are not individual. They are reused when the data is the same (as in your case)

From the doc:

A common mistake people make is to create multiple PendingIntent objects with Intents that only vary in their "extra" contents, expecting to get a different PendingIntent each time. This does not happen. The parts of the Intent that are used for matching are the same ones defined by Intent.filterEquals. If you use two Intent objects that are equivalent as per Intent.filterEquals, then you will get the same PendingIntent for both of them.

Erich
  • 1,800
  • 13
  • 19
  • The extra data does not change the intent, it will be still the same – Erich Jun 01 '16 at 20:17
  • Just found this answer by randomly browsing questions and it answers a bug I have for weeks now! I guess we'll have to create a subclass of intent with overridden filterEquals, eh? – Hubert Grzeskowiak Jun 01 '16 at 20:51
  • Wait a sec. The docs state that different request codes to PendingIntent.getBroadcast deliver distinct pending intents. And the code above does use different request codes. – Hubert Grzeskowiak Jun 01 '16 at 20:54
  • @HubertGrzeskowiak if you find a solution please coment here or post an answer ^^ – John Sardinha Jun 01 '16 at 21:04
  • Different request codes make the `PendingIntent`s unique. `AlarmManager` is triggering both alarms, just at the wrong times. See my answer. – David Wasser Jun 07 '16 at 10:39
-1

It seems like the requestCode field in your getBroadcast call does not always make it unique. See the second comment on this question: What's "requestCode" used for on PendingIntent?

Since the "extra" contents don't differentiate them either, someone found that setting different data on the Intent worked: https://stackoverflow.com/a/33203752/508608

Community
  • 1
  • 1
nicobatu
  • 1,332
  • 2
  • 15
  • 33