Tuesday, 7 April 2015

A Pitfall in PendingIntent and Its Solution

Hello Friends,

              The Android documentation has a nice overview chapter about how to notifiy the user with status bar notifications. The example text works quite nicely and the user gets informed and can then call back into the application. 
Setting up a notification goes along the lines of (taken from the developer guide):

 Intent notificationIntent = new Intent(this, MyClass.class);  
 PendingIntent contentIntent= PendingIntent.getActivity(this,0, notificationIntent, 0);  
 notification.setLatestEventInfo(context, "Title","something went wrong", contentIntent);  

    where a PendingIntent is set up as a "pointer" and stored by the system so that when the user selects the notification in the status bar the target activity specified in the notificationIntent can be called. 
Now sometimes you want to attach some additional data to the intent to be delivered - like a longer explanation why your action failed. You would go like:

 Intent notificationIntent = new Intent(context,MyClass.class);  
  notificationIntent.putExtra("key","value");      
  notificationIntent.putExtra("key2",someCounter++);   

  to add the payload. And in MyClass you would get the data via

 Intent intent = getIntent();      
 Bundle bundle = intent.getExtras();      
 String head = bundle.getString("key");      
 Integer body = bundle.getInt("key2");  

 Now when the notification fires,  the intent is created and attached to the PendingIntent and this shows up in the status bar.

User then selects the status bar to see the longer message and presses this area to see the full details. This means that the system delivers "out of the blue" the created Intent message and thus starts MyClass-activity, which then pulls the payload from the intent. 

When you do this a few times in a row you will see that the passed counter (someCounter) does increase in your sending activity, but that the receiver always shows the initial value. Canceling the notification in the sender does not help here. 

This comes from the fact, that the system does not assume that only because we pass a new Intent object to the pending intent, we want this new intent object to be delivered and just keeps the old (initial) pending intent alive.
To get the desired semantics, we need to pass a flag to tell the system:
 PendingIntent pintent =  PendingIntent.getActivity(context,0,intent,PendingIntent.FLAG_CANCEL_CURRENT);  

This flag (FLAG_CANCEL_CURRENT) tells the system that the old pending intent is no longer valid and it should cancel (=remove) it and then create a fresh one for us.

There are more possible flags, which are described on the javadoc page for PendingIntent.


Thanks,




No comments:

Post a Comment