76

I am having trouble making my class Parcelable. The trouble is, I am trying to write to the parcel a member in the class which is an ArrayList<Parcelable> object. The ArrayList is Serializable, and the objects (ZigBeeDev) in the list are Parcelable.

Here is the relevant code:

package com.gnychis.coexisyst;

import java.util.ArrayList;
import java.util.Iterator;

import android.os.Parcel;
import android.os.Parcelable;

public class ZigBeeNetwork implements Parcelable {

    public String _mac;             // the source address (of the coordinator?)
    public String _pan;             // the network address
    public int _band;               // the channel
    ArrayList<Integer> _lqis;       // link quality indicators (to all devices?)
    ArrayList<ZigBeeDev> _devices;  // the devices in the network

    public void writeToParcel(Parcel out, int flags) {
        out.writeString(_mac);
        out.writeString(_pan);
        out.writeInt(_band);
        out.writeSerializable(_lqis);
        out.writeParcelable(_devices, 0);  // help here
    }

    private ZigBeeNetwork(Parcel in) {
        _mac = in.readString();
        _pan = in.readString();
        _band = in.readInt();
        _lqis = (ArrayList<Integer>) in.readSerializable();
        _devices = in.readParcelable(ZigBeeDev.class.getClassLoader());  // help here
    }

    public int describeContents() {
        return this.hashCode();
    }

    public static final Parcelable.Creator<ZigBeeNetwork> CREATOR = 
            new Parcelable.Creator<ZigBeeNetwork>() {
        public ZigBeeNetwork createFromParcel(Parcel in) {
            return new ZigBeeNetwork(in);
        }

        public ZigBeeNetwork[] newArray(int size) {
            return new ZigBeeNetwork[size];
        }
    };

    //...
}

I have marked two spots "// help here" to understand how to properly write to the parcel and how to reconstruct it. If ZigBeeDev is Parcelable (properly tested), how do I do this properly?

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
gnychis
  • 7,289
  • 18
  • 75
  • 113

5 Answers5

142

You almost got it !

You just need to do :

public void writeToParcel(Parcel out, int flags) {
    out.writeString(_mac);
    out.writeString(_pan);
    out.writeInt(_band);
    out.writeSerializable(_lqis);
    out.writeTypedList(_devices);
}

private ZigBeeNetwork(Parcel in) {
    _mac = in.readString();
    _pan = in.readString();
    _band = in.readInt();
    _lqis = (ArrayList<Integer>) in.readSerializable();
    in.readTypedList(_devices, ZigBeeDev.CREATOR);
}

That's all!

For your list of Integer, you can also do :

out.writeList(_lqis);
_lqis = new ArrayList<>();
in.readList(_lqis Integer.class.getClassLoader());

It should work.

maclir
  • 3,218
  • 26
  • 39
NitroG42
  • 5,336
  • 2
  • 28
  • 32
  • 2
    For future reference, and I'm not sure if it's a typo that no one has noticed, but `ZigBeeDev.Creator` should be `ZigBeeDev.CREATOR`. I got an error every time until I realized there were two different creators I could use in my own project. – gatlingxyz Mar 25 '14 at 18:51
  • @NitroG42 The second approach for read list results into `cannot convert from void to ArrayList ` – AlexAndro Aug 07 '14 at 13:45
  • 8
    I also needed to instantiate the array before as well, example my code would look like: _devices = new ArrayList(); in.readTypedList(_devices, ZigBeeDev.Creator); – sgarman Aug 18 '14 at 23:10
  • @sgarman There's no need to instantiate the array cause then you get the warning: Explicit type argument ZigBeeDev can be replaced with <> This inspection reports all new expressions with type arguments which can be replaced with diamond type <> – crubio Feb 15 '16 at 14:49
  • Confirm to be working with generic `Hashtable` that uses primitives types (including `String`) for either of his keys or values. – Eido95 Jan 23 '17 at 10:47
17

In my case in.readTypedList(_devices, ZigBeeDev.CREATOR); gave me a NullPointerException on _devices. So I used this:

_devices = in.createTypedArrayList(ZigBeeDev.CREATOR);
osrl
  • 8,168
  • 8
  • 36
  • 57
  • 3
    fwiw - the reason you were receiving a NPE is because if you use `in.readTypedList()` you need to create a new ArrayList() first and then pass that to the `in.readTypedList(yourNewArrayList, YourObj.CREATOR)` otherwise it will only be trying to put the data into a null list. – rf43 Oct 24 '15 at 23:50
  • Yes, that's correct. I've read the source code before I used it. Thank you for the extra information which I forgot to write. – osrl Oct 27 '15 at 17:08
  • @osrl when we follow your solution got exception java.lang.OutOfMemoryError: Failed to allocate a 13893804 byte allocation with 11004100 free bytes and 10MB until OOM – Ajit Kumar Dubey Feb 10 '16 at 11:51
  • Does your array hold files or something big? @Ajit – osrl Feb 11 '16 at 10:08
8

You should use writeList(List l) for your list of integers and writeTypedList(List val) for the list of ZigBeeDevices

DoubleMalt
  • 1,263
  • 12
  • 17
2

In constructor you should use

_lqis = in.createTypedArrayList(ZigBeeDev.CREATOR);

And in "writeToParcel" use

out.writeTypedList(_lqis);
Anton Smirnov
  • 89
  • 1
  • 2
0

A little late but I also had this issue. After a long waste of time I stumbled upon the parcelabler.com website which automatically creates parcels for you.

It allowed me to create a nested parcel with an array list inside very easily, and saved me a lot of time.

Basically the way the website works is you enter your object with the ArrayList in it and it automatically adds the necessary methods to make it parcelable (read from parcel, write to parcel, describe contents, and parcel Creator are all generated automatically). This is particularily useful when creating complex parcels, such as the question here asks, which contain nested parcels, arrays, and lists.

EDIT: Also IntelliJ IDEA and Android Studio have plugins for this which do a similar thing to the website listed:

These plugins generate Android Parcelable boilerplate code based on fields in the class.

JFreeman
  • 974
  • 1
  • 10
  • 26