8

Does anyone know if there are any changes to how Android 7.0 (Nougat) handles intent extras compared to Android 6.0 (Lollipop)?

Long story short: my app works as intended on all versions from 4.1(16) to 6.0(23) but crashes on android 7.0(24)!

The app creates a pending intent with an intent to a custom broadcast receiver which has extras. However, on android 7 none of the extras are present in the intent received by the broadcast receiver.

MainActivity.java

Intent intent = new Intent(context, PollServerReceiver.class);

// TODO:  Remove after DEBUGGING is completed!
intent.putExtra("TESTING1", "testing1");
intent.putExtra("TESTING2", "testing2");
intent.putExtra("TESTING3", "testing3");

 // PendingIntent to be triggered when the alarm goes off.
 final PendingIntent pIntent = PendingIntent.getBroadcast(context,
            PollServerReceiver.REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);

// Setup alarm to schedule our service runs.
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, firstRun, freqMilis, pIntent);

PollServerReceiver.java

Bundle extras = intent.getExtras();
Log.d(TAG, "onReceive: TESTING1 = " + extras.getString("TESTING1")); // null here

// None of the three "TESTING*" keys are there!
for (String key : extras.keySet()) {
    Object value = extras.get(key);
    Log.d(TAG, String.format("onReceive extra keys: %s %s (%s)", key, value.toString(), value.getClass().getName()));
}

Stack trace obviously gives the NullPointerException as the cause of crash. It would not be so weird if it would crash among all versions, but in this case its the latest android only. Has anyone got any ideas please?

Note: I have tried creating pending intents with different flags including (0, PendingIntent.FLAG_UPDATE_CURRENT, PendingIntent.FLAG_CANCEL_CURRENT) still got the exact same result.

Konaras
  • 573
  • 6
  • 14
  • 2
    I cannot reproduce your problem using the code from your question, so I am guessing that perhaps the real code does not match the question's code. In your real code, are any of your extras in the form of custom `Parcelable` classes? – CommonsWare Sep 19 '16 at 13:06
  • Yes, besides the three testing String extras I am also storing a Parcelable object in the same intent. Of course it is also missing from the intent received at the Receiver as well the the tree string extras. – Konaras Sep 19 '16 at 13:11
  • 1
    24 doesn't allow to pass a Parcelable object into the AlarmManager , http://stackoverflow.com/questions/38466080/dp5-7-0-does-adding-extras-to-a-pending-intent-fail – Pavneet_Singh Sep 19 '16 at 13:12
  • Oh, god. (┛◉Д◉)┛彡┻━┻ – Konaras Sep 19 '16 at 13:14
  • 2
    In case it's helpful to others: putting an enum value into the intent extras triggers the same behaviour. The solution is to recreate the enum value in the receiver; an easy way would be to pass the ordinal value rather than the enum itself. (If the enum is likely to change, it would be better to use a more long-term stable identifier such as a constant associated with the enum in its constructor.) – Steve Haley Oct 19 '16 at 13:43

2 Answers2

17

Putting a custom Parcelable in a PendingIntent has never been especially reliable, and it flat-out will not work in an AlarmManager PendingIntent on Android 7.0. Other processes may need to fill in values into the Intent, and that involves manipulating the extras, and that can't be done in any process but your own, since no other process has your custom Parcelable class.

This SO answer has a workaround, in the form of converting the Parcelable yourself to/from a byte[].

Community
  • 1
  • 1
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
4

I had a similar problem but I think I found an easy solution. Put your data inside a Bundle and send that Bundle with your intent. In my case I wanted to send a serializable object with my intent.

Setup the alarm:

AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReciever.class);
Bundle bundle = new Bundle();

//creating an example object
ExampleClass exampleObject = new ExampleClass();

//put the object inside the Bundle
bundle.putSerializable("example", exampleObject);

//put the Bundle inside the intent
intent.putExtra("bundle",bundle);

PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);

//setup the alarm
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), alarmIntent);

Receive the alarm:

public class AlarmReciever extends BroadcastReceiver {

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

        // get the Bundle
        Bundle bundle = intent.getBundleExtra("bundle");
        // get the object
        ExampleClass exampleObject = (ExampleClass)bundle.getSerializable("example");
    }

}

It worked fine for me. Hope it helps :)

Abdelilah El Aissaoui
  • 4,204
  • 2
  • 27
  • 47