6

I have some trouble with PendingIntents. Every time my app is opened it will schedule some broadcasts. My Problem is that already existing PendingIntents aren't recognized.

I know that the PendingIntents and the unterlaying Intents must be created with the same parameters.

Her is my code ... started in my Launcher Activity as a Asynctask.

        long nextExecute = getNextExecute(t);

        if (nextExecute > System.currentTimeMillis()) {

            int tt = 12;

            intent = new Intent(context, BirthExecutor.class);
            intent.putExtra(Target.ROW_ID, tt);

            PendingIntent pending = PendingIntent.getBroadcast(context, 10, intent, PendingIntent.FLAG_NO_CREATE);

            if (pending != null) {

                manager.set(AlarmManager.RTC_WAKEUP, nextExecute, pending);

                BirthLog log = new BirthLog("Scheduled " + t.getFirstname() + " " + t.getLastname() + " on " + df.format(nextExecute));
                log_helper.insertLog(log);

            } else {

                BirthLog log = new BirthLog(t.getFirstname() + " " + t.getLastname() + " already active!");
                log_helper.insertLog(log);

            }

            intent = new Intent(LogAdapter.LOG_RECEIVE_ACTION);
            context.sendBroadcast(intent);

        }

The exact same code is executed each time but why is the pending variable not null? It has to be !!! I have hardcoded the requestId also the putExtra is hardcoded.

EDIT

I have another function to update this schedules. Only if I execute that function the PendingIntents aren't recognized any more. I tried to use the same context object as a static reference but also that failed.

public void updateTask(Target t) {

    if (t.getTime() == null || t.getRow() == null)
        return;

    Intent intent;
    SimpleDateFormat df = new SimpleDateFormat("HH:mm dd.MM.yyyy", Locale.US);

    long nextExecute = getNextExecute(t);

    if (nextExecute > System.currentTimeMillis()) {

        intent = new Intent(static_context, BirthExecutor.class);
        intent.putExtra(Target.ROW_ID, 12);

        PendingIntent pending = PendingIntent.getBroadcast(static_context, 10, intent, PendingIntent.FLAG_CANCEL_CURRENT);

        if (pending != null) {

            manager.set(AlarmManager.RTC_WAKEUP, nextExecute, pending);

            BirthLog log = new BirthLog("Scheduled " + t.getFirstname() + " " + t.getLastname() + " on " + df.format(nextExecute));
            log_helper.insertLog(log);
            Log.d("birth", log.getComment());

        } else {

            BirthLog log = new BirthLog(t.getFirstname() + " " + t.getLastname() + " ERROR. TIME: " + df.format(nextExecute));
            log_helper.insertLog(log);
            Log.d("birth", log.getComment());

        }

        intent = new Intent(LogAdapter.LOG_RECEIVE_ACTION);
        static_context.sendBroadcast(intent);

    }

}

EDIT 2

Ok it makes sence if it returns a non-null result if that pending-intent is already scheduled. I have changed my code. Every time I rotate my phone the main activity onCreate is fired and the scheduling asynctask is executed. BUT the result is always null ?!?! It should be a non-null result if there is a scheduled pendingintent already, right?

@Override
protected Void doInBackground(Void... params) {

    Intent intent;
    SimpleDateFormat df = new SimpleDateFormat("HH:mm dd.MM.yyyy", Locale.US);

    for (Target t : target_helper.getAllTarget()) {

        long nextExecute = getNextExecute(t);

        if (nextExecute > System.currentTimeMillis()) {

            PendingIntent pending = getPendingIntent(context, t, false);

            if (pending != null) {

                BirthLog log = new BirthLog(t.getFirstname() + " " + t.getLastname() + " already active!");
                log_helper.insertLog(log);

            } else {

                manager.set(AlarmManager.RTC_WAKEUP, nextExecute, pending);

                BirthLog log = new BirthLog("Scheduled " + t.getFirstname() + " " + t.getLastname() + " on " + df.format(nextExecute));
                log_helper.insertLog(log);

            }

            intent = new Intent(LogAdapter.LOG_RECEIVE_ACTION);
            context.sendBroadcast(intent);

        }

    }

    return null;

}

private PendingIntent getPendingIntent(Context context, Target t, boolean update) {

    Intent intent = new Intent(context, BirthExecutor.class);
    intent.putExtra(Target.ROW_ID, t.getRow());

    if (update) {
        return PendingIntent.getBroadcast(context, t.getRow().intValue(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
    } else {
        return PendingIntent.getBroadcast(context, t.getRow().intValue(), intent, PendingIntent.FLAG_NO_CREATE);
    }

}
Andrei
  • 42,814
  • 35
  • 154
  • 218
Pascal
  • 2,059
  • 3
  • 31
  • 52

3 Answers3

9

The documentation is incorrect. If you use PendingIntent.FLAG_NO_CREATE when calling PendingIntent.getBroadcast() it will return null if no matching PendingIntent is found. Otherwise it will return the matching PendingIntent.

The documentation says it is the other way around, but that's wrong :-(

David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • omg if it is really like that grrr ... I will have a look ... thank you ;) – Pascal Mar 14 '14 at 07:43
  • 4
    Actually, if you think about it for more than a few seconds, it is clear that it makes no sense to return `null` if there is a matching `PendingIntent` and non-null if there isn't one, since by setting `FLAG_NO_CREATE` you've indicated that you **don't want Android to create a new `PendingIntent`**. What should it return as a non-null result? – David Wasser Mar 14 '14 at 09:50
  • did you solve it ? i am facing the exact same problem – Yamen Nassif Jan 17 '15 at 06:31
4

The system does exactly what the documentation says:

int FLAG_NO_CREATE

Flag indicating that if the described PendingIntent does not already exist, then simply return null instead of creating it.

With that said, if I don't want to start my task if it's already running, I do this:

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
if (pendingIntent == null) {
    pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    // start it only it wasn't running already
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 10 * 60 * 1000, pendingIntent);
}
Community
  • 1
  • 1
Andrei
  • 42,814
  • 35
  • 154
  • 218
0

I had the same issue and now I understand why and how to avoid it.

If I run the following lines for the very first time, my pendingIntent is null.

final Intent intentToCancel = new Intent(mContext, receiver);
intentToCancel.setAction(action);
final PendingIntent pendingIntentToCancel =
PendingIntent.getBroadcast(mContext, requestCode, intentToCancel, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_NO_CREATE);

However, if I schedule an alarm for this pendingIntent and then I cancel it, if I create again the pending intent with the lines above, it will not be null. Even if the alarm is cancelled.

The solution is : When I cancel the alarm, I also have to cancel the pending intent like that:

mAlarmManager.cancel(pendingIntentToCancel);
pendingIntentToCancel.cancel();
Thibaut
  • 45
  • 7