Adding the linked issue on tracker: https://code.google.com/p/android/issues/detail?id=216581&thanks=216581&ts=1468962325
So I installed the DP5 Android 7.0 rele
For anyone ending up here pulling your hair out over AlarmManager (and haven't given up and gone to JobScheduler yet), Google in the production API 24 build does not support passing a Parcelable object into the AlarmManager.
The way I got around this: If you need to send a List (or single object) into the AlarmManager, store the item into SharedPreferences as a String. (Gson.toJson(object, type)) If the object is an interface, there are a number of interface adapter solutions out there. One I found floating around S/O:
public final class InterfaceAdapter<T> implements JsonSerializer<T>, JsonDeserializer<T> {
public JsonElement serialize(T object, Type interfaceType, JsonSerializationContext context) {
final JsonObject wrapper = new JsonObject();
wrapper.addProperty("type", object.getClass().getName());
wrapper.add("data", context.serialize(object));
return wrapper;
}
public T deserialize(JsonElement elem, Type interfaceType, JsonDeserializationContext context) throws JsonParseException {
final JsonObject wrapper = (JsonObject) elem;
final JsonElement typeName = get(wrapper, "type");
final JsonElement data = get(wrapper, "data");
final Type actualType = typeForName(typeName);
return context.deserialize(data, actualType);
}
private Type typeForName(final JsonElement typeElem) {
try {
return Class.forName(typeElem.getAsString());
} catch (ClassNotFoundException e) {
throw new JsonParseException(e);
}
}
private JsonElement get(final JsonObject wrapper, String memberName) {
final JsonElement elem = wrapper.get(memberName);
if (elem == null)
throw new JsonParseException("no '" + memberName + "' member found in what was expected to be an interface wrapper");
return elem;
}
}
Once you have the adapter set up, you won't need to set up GS0N each time with the TypeAdapter if you're using some sort of DI framework (i.e. Dagger2) like so...
@Singleton
@Provides
public Gson providesGson() {
return new GsonBuilder()
.registerTypeAdapter(YourInterfaceClass.class, new InterfaceAdapter<YourInterfaceClass>())
.create();
So all you'll have to do is run....
/**
* stores yourInterfaceClass in shared prefs
*/
public void setNextReminder(List<YourInterfaceClass> yourInterfaceClass) {
Type type = new TypeToken<List<YourInterfaceClass>>() {}.getType();
sharedPrefs.edit().putString(YOUR_KEY, gson.toJson(yourInterfaceClass, type)).apply();
}
Hope this helps. Of course, when you need to get this object out of shared prefs....
String json = sharedPrefs.getString(YOUR_KEY, "No object found");
Doing the typical List object = gson.fromJson(json, type) should work.
Cheers.
I've found that wrapping the Parcelable in a Bundle works.
// When setting up the PendingIntent for the AlarmManager:
Intent intent = new Intent(context, MyService.class);
MyParcelable myParcelable = new MyParcelable();
Bundle b = new Bundle();
b.putParcelable(EXTRA_MY_PARCELABLE, myParcelable);
intent.putExtra(EXTRA_BUNDLE, b);
PendingIntent.getService(0, intent, 0);
// From the Service (or Activity, BroadcastReceiver, etc.):
Bundle b = intent.getExtra(EXTRA_BUNDLE);
MyParcelable myParcelable = b.getParcelableExtra(EXTRA_MY_PARCELABLE);
However, I'm not sure how future-proof this approach is. I've commented on the issue on the android bug tracker: https://code.google.com/p/android/issues/detail?id=209422#c11 but I doubt it's going to receive a response since the issue has already been marked closed.
I have seen this sort of behavior reported before, with custom Parcelable
objects and system services (e.g., NotificationManager
). What seems to happen is that the system tries using the PendingIntent
, and as part of that for some reason it tries to un-Parcel
the Parcelable
. This fails, because the system doesn't have your classes. I haven't heard of somebody running into this in a while, but it's entirely possible that there is a regression in Android N that re-introduced it.
You might rummage through LogCat to see if there are any messages — or, better yet, stack traces — from the system (not your app) that seem to pertain to your alarm event.
If you can create a reproducible test case, file an issue on the Android issue tracker. If you think of it, post a link to it here, as I'd like to take a peek at it.
In terms of workarounds, I can think of two:
Don't put the Parcelable
in there. Instead, put an ID that you can use to look up the information as needed, whether from an in-memory cache (if your process happens to still be around) or from whatever your persistent data store is.
Switch from Parcelable
to what I and others have termed "bundle-able", where you convert your object to and from a Bundle
. Basically, stick solely to OS-defined classes, with no custom classes. Then, the system can safely de-Parcel
the Bundle
(for whatever reason it does so). This, of course, is much more painful than simply using an annotation processor to create the Parcelable
implementation.