28

Does anyone know how to add an ArrayList<NameValuePair> to an Intent as an extra?

ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("first_name", first_name));
nameValuePairs.add(new BasicNameValuePair("last_name", last_name));
nameValuePairs.add(new BasicNameValuePair("email", email));
nameValuePairs.add(new BasicNameValuePair("password", password));

/* Move on to step 2 */
Intent intent = new Intent(RegisterActivity1.this, RegisterActivity2.class);
intent.putExtra("nvp", nameValuePairs);
startActivity(intent);

Here is the class declaration for RegisterActivity2:

public class RegisterActivity2 extends Activity implements Serializable {
}

The error in the logcat is:

> Parcel: unable to marshal value first_name="whatever"

After implementing the suggestion by Ted Hopp, I still get an error. Here is the stack trace:

08-04 22:10:16.095: E/AndroidRuntime(5065): FATAL EXCEPTION: main
> 08-04 22:10:16.095: E/AndroidRuntime(5065):
> java.lang.RuntimeException: Unable to start activity
> ComponentInfo{/.RegisterActivity2}: java.lang.RuntimeException:
> Parcelable encountered IOException reading a Serializable object (name
> = database.NVP) 08-04 22:10:16.095: E/AndroidRuntime(5065):   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2070)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2095)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> android.app.ActivityThread.access$600(ActivityThread.java:135) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> android.app.ActivityThread$H.handleMessage(ActivityThread.java:1201)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> android.os.Handler.dispatchMessage(Handler.java:99) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> android.os.Looper.loop(Looper.java:137) 08-04 22:10:16.095:
> E/AndroidRuntime(5065):   at
> android.app.ActivityThread.main(ActivityThread.java:4849) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> java.lang.reflect.Method.invokeNative(Native Method) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> java.lang.reflect.Method.invoke(Method.java:511) 08-04 22:10:16.095:
> E/AndroidRuntime(5065):   at
> com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:795)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> com.android.internal.os.ZygoteInit.main(ZygoteInit.java:562) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> dalvik.system.NativeStart.main(Native Method) 08-04 22:10:16.095:
> E/AndroidRuntime(5065): Caused by: java.lang.RuntimeException:
> Parcelable encountered IOException reading a Serializable object (name
> = database.NVP) 08-04 22:10:16.095: E/AndroidRuntime(5065):   at android.os.Parcel.readSerializable(Parcel.java:2144) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> android.os.Parcel.readValue(Parcel.java:2016) 08-04 22:10:16.095:
> E/AndroidRuntime(5065):   at
> android.os.Parcel.readListInternal(Parcel.java:2235) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> android.os.Parcel.readArrayList(Parcel.java:1655) 08-04 22:10:16.095:
> E/AndroidRuntime(5065):   at
> android.os.Parcel.readValue(Parcel.java:1986) 08-04 22:10:16.095:
> E/AndroidRuntime(5065):   at
> android.os.Parcel.readMapInternal(Parcel.java:2226) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> android.os.Bundle.unparcel(Bundle.java:223) 08-04 22:10:16.095:
> E/AndroidRuntime(5065):   at
> android.os.Bundle.getSerializable(Bundle.java:1254) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> android.content.Intent.getSerializableExtra(Intent.java:4268) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> .RegisterActivity2.onCreate(RegisterActivity2.java:24) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> android.app.Activity.performCreate(Activity.java:5244) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     at
> android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1082)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2034)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   ... 11 more 08-04
> 22:10:16.095: E/AndroidRuntime(5065): Caused by:
> java.io.InvalidClassException:
> org.apache.http.message.BasicNameValuePair; IllegalAccessException
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> java.io.ObjectStreamClass.resolveConstructorClass(ObjectStreamClass.java:694)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> java.io.ObjectStreamClass.newInstance(ObjectStreamClass.java:655)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1816)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
> 08-04 22:10:16.095: E/AndroidRuntime(5065):   at
> android.os.Parcel.readSerializable(Parcel.java:2142) 08-04
> 22:10:16.095: E/AndroidRuntime(5065):     ... 23 more
blueware
  • 5,205
  • 1
  • 40
  • 60
AndroidDev
  • 20,466
  • 42
  • 148
  • 239

5 Answers5

24

As others have noted, when you want to pass an array as an intent extra, the elements need to implement Serializable. In your case, the approach is fairly simple (not quite — see below): define your own subclass of BasicNameValuePair and declare it to implement the interface.

public class NVP extends BasicNameValuePair implements Serializable {
    public NVP(String name, String value) {
        super(name, value);
    }
}

Then you can use it like you've been trying:

ArrayList<NVP> nameValuePairs = new ArrayList<NVP>();
nameValuePairs.add(new NVP("first_name", first_name));
nameValuePairs.add(new NVP("last_name", last_name));
nameValuePairs.add(new NVP("email", email));
nameValuePairs.add(new NVP("password", password));

/* Move on to step 2 */
Intent intent = new Intent(RegisterActivity1.this, RegisterActivity2.class);
intent.putExtra("nvp", nameValuePairs);
startActivity(intent);

On the receiving end, you'll need to pull it out with:

ArrayList<NVP> nameValuePairs = (ArrayList<NVP>) intent.getSerializable("nvp");

EDIT Well, the above won't work because BasicNameValuePair doesn't have a default constructor and the serialization mechanism requires non-serializable ancestor classes to have a default constructor. Otherwise you'll get an exception on deserialization.

Unfortunately, this means that the solution isn't as simple as I made out. One work-around is to define your own class to implement Parcelable, as @keerthana murugesan suggests in his answer. If you've implemented Parcelable before, you know it's a bit of a pain. Another work-around is to define a class that wraps a BasicNameValuePair (instead of deriving from it) and manages its own serialization:

public class NVP implements NameValuePair, Serializable {
    private BasicNameValuePair nvp;

    public NVP(String name, String value) {
        nvp = new BasicNameValuePair(name, value);
    }

    @Override
    public String getName() {
        return nvp.getName();
    }

    @Override
    public String getValue() {
        return nvp.getValue();
    }

    // serialization support

    private static final long serialVersionUID = 1L;

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeString(nvp.getName());
        out.writeString(nvp.getValue());
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        nvp = new BasicNameValuePair(in.readString(), in.readString());
    }

    private void readObjectNoData() throws ObjectStreamException {
        // nothing to do
    }
}
Community
  • 1
  • 1
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • @usr55410 - It wasn't quite perfect. I suggested subclassing `NameValuePair`, which wasn't going to work. Fixed now. I assume that you figured out that part yourself, since you already accepted this as a solution. :) – Ted Hopp Aug 05 '13 at 03:41
  • Yes, I did. However there is one more problem. When I test this, I get an error "Parcelable encountered IOException reading a serializable object". My NVP class does implement Serializable. Any ideas? – AndroidDev Aug 05 '13 at 03:49
  • @usr55410 - That's weird. `BasicNameValuePair` is a pretty vanilla class. Can you edit your question to post the stack trace and the line in your code that generated the exception? – Ted Hopp Aug 05 '13 at 04:03
  • @usr55410 - Oh, crap. The `Serializable` mechanism requires that all each ancestor class have a default constructor, and `BasicNameValuePair` doesn't. Let me come up with an `NVP` class that should work. – Ted Hopp Aug 05 '13 at 04:31
  • @usr55410 - Okay, I posted what I hope is a fix. I didn't test it, but if it doesn't work, you should be able to beat it around the ears with a 2x4 until it does. – Ted Hopp Aug 05 '13 at 04:46
  • @TedHopp - I'm running into you again! Thanks for your help on my other question here: http://stackoverflow.com/q/26006360/1735836. I'd like to ask the question for how to make the callback serializable, because I'm getting this issue with my callback. I'll try to figure out how to ask it without getting pummeled with down-votes. – Patricia Sep 24 '14 at 20:23
  • 1
    @Lucy - Before composing your question, you might find it worthwhile to scan through [this thread on `LocalBroadcastReceiver`](http://stackoverflow.com/questions/8802157/how-to-use-localbroadcastmanager) and [this thread on serializing a `Bitmap`](http://stackoverflow.com/questions/5871482/serializing-and-de-serializing-android-graphics-bitmap-in-java). Using a local broadcast is a completely different way of decoupling your pojo from other parts of your app. – Ted Hopp Sep 24 '14 at 20:29
  • @TedHopp - Oh wise one! Master of so much! You saved my @$$ again. Thank you for the tip....that worked. YAY YOU!! :-) – Patricia Sep 25 '14 at 13:57
23

I had the same problem, trying to send objects in ArrayList without implementing Seiralizable in Object class.

The shortcut solution was to envelop ArrayList in Bundle.

So, in the first activity ->

ArrayList<Object> objects = new ArrayList<Object>(); 
// Object class does not implement Serializable interface

Bundle extra = new Bundle();
extra.putSerializable("objects", objects);

Intent intent = new Intent(getBaseContext(), ShowSpread.class);
intent.putExtra("extra", extra);

Second activity ->

Bundle extra = getIntent().getBundleExtra("extra");
ArrayList<Object> objects = (ArrayList<Object>) extra.getSerializable("objects")

Works fine.

Ogmi
  • 257
  • 2
  • 6
1

You need to pass a Object array as a Serializable into the intent. The Object array must consist of objects that are inturn Serializable.

ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("first_name", first_name));
nameValuePairs.add(new BasicNameValuePair("last_name", last_name));
nameValuePairs.add(new BasicNameValuePair("email", email));
nameValuePairs.add(new BasicNameValuePair("password", password));

Here BaseNameValuePair needs to be a class that implements Serializable. Use toArray of ArrayList into put, and typecast it to Serializable. In your getter Activity use getSerializable method on the intent.

taxeeta
  • 1,188
  • 1
  • 12
  • 25
0

For this to work, the genericized type your List holds must implement Serializable.

Karakuri
  • 38,365
  • 12
  • 84
  • 104
  • Thank you. I've added the class declaration for the next activity above. Is this what you are referring to? I still get the same error. – AndroidDev Aug 05 '13 at 03:13
  • No, the class that your List holds is `BasicNameValuePair`, which does not implement `Serializable`. That is your problem. – Karakuri Aug 05 '13 at 03:21
0

This can be achieved using Parcelable interface.

  1. Make your BasicNameValuePair class to implement Parcelable interface.see this for more reference.

    public class BasicNameValuePair implements Parcelable{
    
    }
    
  2. Add these objects to an ArrayList as you did above,

    ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
    nameValuePairs.add(new BasicNameValuePair("first_name", first_name));
    nameValuePairs.add(new BasicNameValuePair("last_name", last_name));
    nameValuePairs.add(new BasicNameValuePair("email", email));
    nameValuePairs.add(new BasicNameValuePair("password", password));
    
  3. Pass these into intent using putParcelableArrayListExtra().

    intent.putParcelableArrayListExtra ("nvp", nameValuePairs);
    startActivity(intent);
    
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521