3

I have an app which uses AlarmManager to schedule alarms for user notification. I pass an instance of my Alarm object to the PendingIntent which is used by AlarmManager:

public void scheduleAlarms(List<Alarm> alarms)
{
    for (Alarm alarm : alarms)
    {           
        Log.d(Constants.EVERY_OTHER_ALARM_APP_LOG_TAG, 
                "Setting alarm for: " + new Date(alarm.getTime()).toString() + 
                " with an interval of " + 
                IntervalEnumStringProvider.getIntervalStringInGivenFormat(
                        alarm.getInterval(), 
                        true));
        PendingIntent alarmScheduledPending = createPendingIntent(alarm);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, 
                alarm.getTime(), 
                (alarm.getInterval() * Constants.ONE_SECOND_IN_MILLIS), 
                alarmScheduledPending);
    }
}

private PendingIntent createPendingIntent(Alarm alarm)
{
    alarmBroadcastReceiverIntent = new Intent(context, AlarmBroadcastReceiver.class);
    alarmBroadcastReceiverIntent.putExtra(Constants.SCHEDULED_ALARM_TAG, alarm);
    return PendingIntent.getBroadcast(context, 
                alarm.getIdForPendingIntent(), 
                alarmBroadcastReceiverIntent, 
                PendingIntent.FLAG_UPDATE_CURRENT);
}

When the alarm time occurs, my AlarmBroadcastReceiver class correctly receives the broadcast but LogCat warns that the Alarm class was not found:

06-29 08:30:00.084: W/Intent(66): Failure filling in extras
06-29 08:30:00.084: W/Intent(66): java.lang.RuntimeException: Parcelable encounteredClassNotFoundException reading a Serializable object (name = com.fastplanet.everyotheralarmapp.alarm.Alarm)
06-29 08:30:00.084: W/Intent(66):   at android.os.Parcel.readSerializable(Parcel.java:1890)
06-29 08:30:00.084: W/Intent(66):   at android.os.Parcel.readValue(Parcel.java:1761)
06-29 08:30:00.084: W/Intent(66):   at android.os.Parcel.readMapInternal(Parcel.java:1947)
06-29 08:30:00.084: W/Intent(66):   at android.os.Bundle.unparcel(Bundle.java:169)
06-29 08:30:00.084: W/Intent(66):   at android.os.Bundle.putAll(Bundle.java:242)
06-29 08:30:00.084: W/Intent(66):   at android.content.Intent.fillIn(Intent.java:4530)
06-29 08:30:00.084: W/Intent(66):   at com.android.server.am.PendingIntentRecord.send(PendingIntentRecord.java:185)
06-29 08:30:00.084: W/Intent(66):   at android.app.PendingIntent.send(PendingIntent.java:400)
06-29 08:30:00.084: W/Intent(66):   at com.android.server.AlarmManagerService$AlarmThread.run(AlarmManagerService.java:636)
06-29 08:30:00.084: W/Intent(66): Caused by: java.lang.ClassNotFoundException: com.fastplanet.everyotheralarmapp.alarm.Alarm
06-29 08:30:00.084: W/Intent(66):   at java.lang.Class.classForName(Native Method)
06-29 08:30:00.084: W/Intent(66):   at java.lang.Class.forName(Class.java:237)
06-29 08:30:00.084: W/Intent(66):   at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:2595)
06-29 08:30:00.084: W/Intent(66):   at java.io.ObjectInputStream.readNewClassDesc(ObjectInputStream.java:1848)
06-29 08:30:00.084: W/Intent(66):   at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:852)
06-29 08:30:00.084: W/Intent(66):   at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:2006)
06-29 08:30:00.084: W/Intent(66):   at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:956)
06-29 08:30:00.084: W/Intent(66):   at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2289)
06-29 08:30:00.084: W/Intent(66):   at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2243)
06-29 08:30:00.084: W/Intent(66):   at android.os.Parcel.readSerializable(Parcel.java:1884)
06-29 08:30:00.084: W/Intent(66):   ... 8 more
06-29 08:30:00.084: W/Intent(66): Caused by: java.lang.NoClassDefFoundError: com.fastplanet.everyotheralarmapp.alarm.Alarm
06-29 08:30:00.084: W/Intent(66):   ... 18 more
06-29 08:30:00.084: W/Intent(66): Caused by: java.lang.ClassNotFoundException: com.fastplanet.everyotheralarmapp.alarm.Alarm in loader dalvik.system.PathClassLoader@4001ad90
06-29 08:30:00.084: W/Intent(66):   at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:243)
06-29 08:30:00.084: W/Intent(66):   at java.lang.ClassLoader.loadClass(ClassLoader.java:573)
06-29 08:30:00.084: W/Intent(66):   at java.lang.ClassLoader.loadClass(ClassLoader.java:532)
06-29 08:30:00.084: W/Intent(66):   ... 18 more

However, the BroadcastReceiver successfully unpacks the Alarm object from the received intent and the app works as expected.

What's going on?

UPDATE - added manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.fastplanet.everyotheralarmapp"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="4"
        android:targetSdkVersion="4" />

    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application
        android:name=".data.TheEveryOtherAlarmAppApplication"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <receiver android:name="com.fastplanet.everyotheralarmapp.alarm.AlarmBroadcastReceiver" >
        </receiver>
        <receiver android:name=".alarm.BootReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
            </intent-filter>
        </receiver>

        <activity android:name=".TheEveryOtherAlarmAppActivity" 
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".NewAlarmActivity"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.Dialog" >
        </activity>
        <activity
            android:name=".About"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.Dialog" >
        </activity>
        <activity
            android:name=".AlarmAlertDialogActivity"
            android:excludeFromRecents="true"
            android:noHistory="true"
            android:taskAffinity=""
            android:theme="@android:style/Theme.NoDisplay" >
        </activity>
        <activity
            android:name=".AlarmDetailsActivity" 
            android:screenOrientation="portrait" >
        </activity>
    </application>
</manifest>

UPDATE 2 - adding Alarm class

public class Alarm implements Serializable
{

    private static final long serialVersionUID = -5410846652701834321L;
    private int id = 0;
    private String name = "";
    private long interval = 0;
    private long time;
    private String description;

    public Alarm(String name, long time, long interval, String description, int id)
    {
        this.name = name;
        this.time = time;
        this.interval = interval;
        this.description = description;
        this.id = id;
    }

    public void setInterval(long newAlarmInterval)
    {
        this.interval = newAlarmInterval;
    }

    public long getInterval()
    {
        return interval;
    }

    public void setTime(long time)
    {
        this.time = time;
    }

    public long getTime()
    {
        return time;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }

    public void setDescription(String newAlarmDescription)
    {
        this.description = newAlarmDescription;
    }

    public String getDescription()
    {
        return description;
    }

    public int getIdForPendingIntent()
    {
        return id;
    }

    @Override
    public String toString()
    {
        return "name: " + name + ", time: " 
            + new Date(time).toLocaleString()
            + ", interval: "
            + IntervalEnumStringProvider.getIntervalStringInGivenFormat(interval, true)
            + ", description: " + description
            + ", id: " + id;
    }

    @Override
    public Alarm clone() throws CloneNotSupportedException
    {
        return new Alarm(new String(name), time, interval, new String(description), id);
    }
}
t0mm13b
  • 34,087
  • 8
  • 78
  • 110
barry
  • 4,037
  • 6
  • 41
  • 68

5 Answers5

8

I struggled with this problem. The solution is (credit to mazur here):

This is a defect in android. My target API is 19, and minimum API is 11. the defect is entered here: "ClassNotFoundException when using custom Parcelable" https://code.google.com/p/android/issues/detail?id=6822

when registering alarm:

Bundle bundle = new Bundle();
bundle.putParcelable("com.foo.alarm", alert);
mNotificationReceiverIntent.putExtra("com.foo.alarm",bundle);
mNotificationReceiverPendingIntent = PendingIntent.getBroadcast(this,
        0, mNotificationReceiverIntent, 0);

when receiving the alarm,

    Bundle oldBundle = intent.getBundleExtra("com.foo.alarm");      
    Alert alert = oldBundle.getParcelable("com.foo.alarm");
likejudo
  • 3,396
  • 6
  • 52
  • 107
  • I just debugged a similar problem with ClassNotFound when unpacking extras from an Intent. I found that the problem manifests (no pun intended) when the Intent is broadcast from a different thread and then received on the main (UI) thread. Moving the broadcast of the intent into the UI thread so that the broadcast does not travel across threads resolved the problem! – RobP Jun 12 '14 at 16:35
  • Looks similar to my issue but I'm using Serializable, not Parcelable – barry Apr 18 '16 at 15:40
0

as

ClassNotFoundException: com.fastplanet.everyotheralarmapp.alarm.Alarm in loader

<receiver android:name=".alarm.BootReceiver" >
Dheeresh Singh
  • 15,643
  • 3
  • 38
  • 36
  • in menifest AlarmBroadcastReceiver already defiend as receiver – rajpara Jun 29 '12 at 08:43
  • The BootReceiver receiver is used to catch the device startup broadcast. The Alarm object is not a receiver, just a simple object which is passed to the AlarmBroadcastReceiver. – barry Jun 29 '12 at 08:44
  • is this a class you have "Alarm" – rajpara Jun 29 '12 at 08:45
  • Yes, Alarm is a simple object with a few primitive fields and a couple of strings. – barry Jun 29 '12 at 08:46
  • did you extends it with activity ? because your logcat mentioning that "com.fastplanet.everyotheralarmapp.alarm.Alarm" is not found – rajpara Jun 29 '12 at 08:47
  • No, it's not an Activity. I'll post it. – barry Jun 29 '12 at 08:49
  • It is passed as an EXTRA in the Intent. The error shows that it is having a problem when unparcelling it. It is just a parameter that is passed to the BroadcastReceiver – David Wasser Jun 29 '12 at 08:53
  • Yes, it is an extra in the Intent and seems to be successfully unparcelled. Any idea what the problem is? – barry Jun 29 '12 at 08:56
  • check out this pastebin http://pastebin.com/8SFiqhQP & http://stackoverflow.com/q/2736389/582571 for sample code and check wheather you implement in the same way – rajpara Jun 29 '12 at 09:04
  • I think I'm doing it all correctly. As I say, the code works, it's just that LogCat has warnings – barry Jun 29 '12 at 09:11
  • sorry ... I was not here ... yup I thought initially is it Receiver – Dheeresh Singh Jun 29 '12 at 09:41
0

If you want to pass your alarm object as an EXTRA in an Intent then your Alarm class needs to implement Parcelable, not Serializable.

EDIT:

Seems that this isn't true. It looks like you can use Serializable. However the OP can't get this to work and there are other posts on SE where people have had problems getting this to work.

David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • I'm using `public Intent putExtra(String name, Serializable value)`, which takes a Serializable – barry Jun 29 '12 at 08:59
  • How are you extracting the extra from the Intent when you receive it? – David Wasser Jun 29 '12 at 09:03
  • `Alarm alarmJustSounded = (Alarm) intent.getExtras().getSerializable(Constants.SCHEDULED_ALARM_TAG);` – barry Jun 29 '12 at 09:04
  • Looks to me like it isn't being successfully unparceled. There are several posts on SE where people complain this doesn't work (although also several that say it does work). One post says that ProGuard mangles the class names so that this doesn't work. Another though would be to try to extract the EXTRA using Alarm `alarmJustSounded = (Alarm)intent.getSerializableExtra(Constants.SCHEDULED_ALARM_TAG);` – David Wasser Jun 29 '12 at 09:09
  • Refer http://stackoverflow.com/questions/7527670/android-problem-with-the-serializable-interface http://stackoverflow.com/questions/6014806/android-classnotfoundexception-when-passing-serializable-object-to-activity http://stackoverflow.com/questions/5866093/android-deserialize-object – David Wasser Jun 29 '12 at 09:10
  • Ah! I see the problem! Your Alarm class doesn't have a constructor with zero arguments. Serializable needs this to be able to deserialize the object on the receiving end. – David Wasser Jun 29 '12 at 09:13
  • `getSerializableExtra` gives the same warning in LogCat – barry Jun 29 '12 at 09:18
  • See my previous comment about the empty constructor. – David Wasser Jun 29 '12 at 09:23
  • I've added the empty constructor but the problem remains. – barry Jun 29 '12 at 09:25
0

Working with ADT17, I have the same problem, BUT the BroadcastReceiver wouldn't start. I tried stuff discussed on SO: Tried to change Proguard Configuration, but turns out Proguard is disabled by default. Tried to implement Parcelable but the problem still remains. The only thing that worked was to place the BroadCastReceiver into the same package where the Class of the to-be-received object resides. Note that the exception is still thrown as warning, just as the OP describes.

sulai
  • 5,204
  • 2
  • 29
  • 44
0

I had the same problem, Android 2.3.

Passing a serializable extra to the BroadcastReceiver now works for me after I made the following changes:

  • Create an Intent with an Action string for the PendingIntent.
  • Register my BroadcastReceiver statically in the AndroidManifest.
Miriam
  • 1,178
  • 2
  • 13
  • 23