Intent extra gets cleared out from unknown reason

2019-08-28 12:56发布

问题:

I have Activity A which starts Activity B with following code:

Intent intent = new Intent(this, B.class);
intent.putExtra("foo", new MySerializableObject());
startActivity(intent);

In B "foo" is received correctly and then I create PendingIntent to start itself after some time, you can think about it as some alarm clock app. Anyway the mysterious thing is that when I schedule this intent in following way:

Intent intent = new Intent(context, B.class);
intent.putExtra("bar", true);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
        PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 
        SystemClock.elapsedRealtime() + delayMs, pendingIntent);

Then everything is fine (after receiving this intent "bar" value is true), however if I add following line before or after "bar":

intent.putExtra("foo", new MySerializableObject());

Then when I receive this intent both "foo" and "bar" are missing. I mean false is returned from both of those lines:

getIntent().hasExtra("foo")
getIntent().hasExtra("bar")

What could be the reason of such behaviour?

EDIT: Basing on suggestion in comments I've tried:

intent.putExtra("foo", true);
intent.putExtra("bar", true);

and it worked, so I thought that maybe there is something wrong with MySerializableObject, so this is what I've tried next:

intent.putExtra("foo",
        new Serializable() {
            @Override
            public int hashCode() { return super.hashCode(); }
            });
intent.putExtra("bar", true);

But this causes exactly the same problem as I described ("foo" and "bar") are missing. Finally I've also tried replacing "foo" with "xxx" but it didn't change anything, so to me it looks like some weird Android bug.

回答1:

As suggested by android-hub I've tried using Bundle but the problem is the same. This works ("bar" is available):

    Bundle b = new Bundle();
    b.putBoolean("bar", true);
    intent.putExtras(b);

while this doesn't (both "xxx" and "bar" don't exist):

    Bundle b = new Bundle();
    b.putSerializable("xxx", new Serializable() {});
    b.putBoolean("bar", true);
    intent.putExtras(b);

The mentioned answer explains a bit what is going on, however as you see suggested solution (with Bundle) doesn't work. Anyway I guess this part is a correct explanation:

if a core OS process needs to modify the Intent extras, that process winds up trying to recreate your Serializable objects as part of setting up the extras Bundle for modification. That process does not have your class and so it gets a runtime exception.

And after reading that I saw this in logs:

W/Intent: Failure filling in extras java.lang.RuntimeException: Parcelable encountered ClassNotFoundException reading a Serializable object (name = test.edu.B$1)

Previously I missed that because I was only looking at logs from my app. Anyway I will probably have to decompose MySerializableObject to native types (Strings, int arrays etc) create special keys for them and etc, which I guess will work but is painful. I don't want to use Parcelable (even if it would work) because I don't want my model classes to be Android specific.

The only thing I can say after wasting couple hours of your and my time to identify this problem is that Google made really crappy design in that particular case. In my opinion PendingIntent should check if some serializable is added to intent and throw some unsupported exception already in my app or there shouldn't be API for adding Serializable to Intent in the first place if it can't be handled well with other Android components.