Here is the problem. My program is running perfect in Android 6.0. After update the device to android 7.0. Pendingintent can not pass the parcelable data to boradcast reveiver. Here is the code.
Fire the alarm
public static void setAlarm(@NonNull Context context, @NonNull Todo todo) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra("KEY_TODO", todo);
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, todo.remindDate.getTime(), alarmIntent);
}
Todo is a Parcelable class while todo is the instance I need in notification.
In Broadcastreceiver, I cannot getParcelable data.
public void onReceive(Context context, Intent intent) {
Todo todo = intent.getParcelableExtra("KEY_TODO");
}
Here is the result of intent when I debug
I dont know why the intent only contains a Integer that I never put it in. Where is the Parcelable todo. This code has no problem in android 6.0, but can not run in 7.0
A pendingIntent uses the following method to handle different types of intent like PendingIntent.getActivity() — This will start an Activity like calling context.startActivity()PendingIntent.getBroadcast() — This will perform a Broadcast like calling context.sendBroadcast()PendingIntent.getService() — This will start a ...
FLAG_IMMUTABLE : Indicates the Intent inside the PendingIntent cannot be modified by other apps that pass an Intent to PendingIntent.send() . An app can always use FLAG_UPDATE_CURRENT to modify its own PendingIntents. Prior to Android 12, a PendingIntent created without this flag was mutable by default.
what is the use of pending intent? A PendingIntent is a token that you give to a foreign application (e.g. NotificationManager, AlarmManager, Home Screen AppWidgetManager, or other 3rd party applications), which allows the foreign application to use your application's permissions to execute a predefined piece of code.
Quoting myself:
Custom
Parcelable
classes — ones unique to your app, not a part of the Android framework — have had intermittent problems over the years when used asIntent
extras. Basically, if a core OS process needs to modify theIntent
extras, that process winds up trying to recreate yourParcelable
objects as part of setting up the extrasBundle
for modification. That process does not have your class and so it gets a runtime exception.One area where this can occur is with
AlarmManager
. Code that used customParcelable
objects withAlarmManager
that might have worked on older versions of Android will not work on Android N.
The most efficient workaround that I know of is to manually convert the Parceable
yourself into a byte[]
and put that in the Intent
extra, manually converting it back into a Parcelable
as needed. This Stack Overflow answer
shows the technique, and this sample project provides a complete working sample.
The key bits are the conversions between the Parcelable
and the byte[]
:
/***
Copyright (c) 2016 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
From _The Busy Coder's Guide to Android Development_
https://commonsware.com/Android
*/
package com.commonsware.android.parcelable.marshall;
import android.os.Parcel;
import android.os.Parcelable;
// inspired by https://stackoverflow.com/a/18000094/115145
public class Parcelables {
public static byte[] toByteArray(Parcelable parcelable) {
Parcel parcel=Parcel.obtain();
parcelable.writeToParcel(parcel, 0);
byte[] result=parcel.marshall();
parcel.recycle();
return(result);
}
public static <T> T toParcelable(byte[] bytes,
Parcelable.Creator<T> creator) {
Parcel parcel=Parcel.obtain();
parcel.unmarshall(bytes, 0, bytes.length);
parcel.setDataPosition(0);
T result=creator.createFromParcel(parcel);
parcel.recycle();
return(result);
}
}
Following the suggesting of @David Wasser in the comments underneath the question, I was able to resolve the same problem like this:
public static void setAlarm(@NonNull Context context, @NonNull Todo todo) {
...
Intent intent = new Intent(context, AlarmReceiver.class);
Bundle todoBundle = new Bundle();
todoBundle.putParcelable("KEY_TODO", todo);
intent.putExtra("KEY_TODO", todoBundle); // i just reuse the same key for convenience
...
}
Then in the broadcast receiver extract the bundle like this:
public void onReceive(Context context, Intent intent) {
Bundle todoBundle = intent.getBundleExtra("KEY_TODO");
Todo todo;
if (todoBundle != null ) {
todo = todoBundle.getParcelable("KEY_TODO");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With