1

I can't see the error, I'm having this problem for a long time already... My parcelable class crashes if it is recreated, but I can't find the problem...

  • I checked the order of writing/reading data.
  • I checked the functions I use (direct reading/writing vs my custum null save functions)

I marked the line in the first code block that created following exception: java.lang.ArrayIndexOutOfBoundsException: length=5; index=5

SHORT CODE

private Set<ContactType> mMatchesDataLoaded = new HashSet<ContactType>();

saving the set

dest.writeInt(mMatchesDataLoaded.size());
Iterator<ContactType> it = mMatchesDataLoaded.iterator();
while (it.hasNext())
    dest.writeInt(it.next().ordinal());

reading the set

int count = source.readInt();
for (int i = 0; i < count; i++)
// ---------------------------------------------------------------------------
// next line produces EXCEPTION!!! java.lang.ArrayIndexOutOfBoundsException: length=5; index=5
// ---------------------------------------------------------------------------
    mMatchesDataLoaded.add(ContactType.values()[source.readInt()]); 

FULL CODE

I can't see any problems or rather where the problem is...

public class ContactPhone implements Parcelable
{
    public static enum ContactType
    {
        WhatsApp,
        Viber,
        GooglePlus,
        Twitter,
        Instagram
    }

    private boolean mIsUserProfile = false;
    private boolean mHasImage;
    private int mId;
    private long mRawId;
    private String mName = null;
    private List<PhoneNumber> mNumbers = new ArrayList<PhoneNumber>();
    private DBPhoneContact mDBContact = null;

    private Set<ContactType> mMatchesDataLoaded = new HashSet<ContactType>();
    private HashMap<ContactType, List<BaseMatchContact>> mMatchesData = new HashMap<ContactType, List<BaseMatchContact>>();

    // ----------------------
    // Parcelable
    // ----------------------

    @Override
    public void writeToParcel(Parcel dest, int flags)
    {
        ParcelBundleUtils.writeBoolean(dest, mIsUserProfile);
        ParcelBundleUtils.writeBoolean(dest, mHasImage);
        ParcelBundleUtils.writeIntegerNullSafe(dest, mId);
        ParcelBundleUtils.writeLongNullSafe(dest, mRawId);
        ParcelBundleUtils.writeStringNullSafe(dest, mName);
        dest.writeList(mNumbers);

        ParcelBundleUtils.writeLongNullSafe(dest, mDBContact != null ? mDBContact.getId() : null);

        // save set
        dest.writeInt(mMatchesDataLoaded.size());
        Iterator<ContactType> it = mMatchesDataLoaded.iterator();
        while (it.hasNext())
            dest.writeInt(it.next().ordinal());

        // save HashMap
        dest.writeInt(mMatchesData.size());
        for (Map.Entry<ContactType, List<BaseMatchContact>> entry : mMatchesData.entrySet())
        {
            dest.writeInt(entry.getKey().ordinal());
            dest.writeInt(entry.getValue().size());
            for (int i = 0; i < entry.getValue().size(); i++)
                dest.writeParcelable(entry.getValue().get(i), 0);
        }
    }

    public void readFromParcel(Parcel source)
    {
        mIsUserProfile = ParcelBundleUtils.readBoolean(source);
        mHasImage = ParcelBundleUtils.readBoolean(source);
        mId = ParcelBundleUtils.readIntegerNullSafe(source);
        mRawId = ParcelBundleUtils.readLongNullSafe(source);
        mName = ParcelBundleUtils.readStringNullSafe(source);
        source.readList(mNumbers, PhoneNumber.class.getClassLoader());

        Long id = ParcelBundleUtils.readLongNullSafe(source);
        mDBContact = null;
        if (id != null)
            mDBContact = MainApp.getDS().getDBPhoneContactDao().load(id);

        // read set
        int count = source.readInt();
        for (int i = 0; i < count; i++)
// ---------------------------------------------------------------------------
// next line produces EXCEPTION!!! java.lang.ArrayIndexOutOfBoundsException: length=5; index=5
// ---------------------------------------------------------------------------
            mMatchesDataLoaded.add(ContactType.values()[source.readInt()]); 

        // read HashMap
        count = source.readInt();
        for (int i = 0; i < count; i++)
        {
            ContactType type = ContactType.values()[source.readInt()];
            Class<?> clazz = BaseDef.getMatchClass(type);
            // L.d(this, "Classloader: " + clazz.getName() + " type: " + type.name());
            int size = source.readInt();
            List<BaseMatchContact> list = new ArrayList<BaseMatchContact>();
            for (int j = 0; j < size; j++)
                list.add((BaseMatchContact) source.readParcelable(clazz.getClassLoader()));
            mMatchesData.put(type, list);
        }
    }
}

The PhoneNumber class implements parcelable and is quite simple and read/writes like following:

@Override
public void writeToParcel(Parcel dest, int flags)
{
    ParcelBundleUtils.writeStringNullSafe(dest, mName);
    ParcelBundleUtils.writeStringNullSafe(dest, mNormNumber);
    ParcelBundleUtils.writeStringNullSafe(dest, mNumber);
}

public void readFromParcel(Parcel source)
{
    mName = ParcelBundleUtils.readStringNullSafe(source);
    mNormNumber = ParcelBundleUtils.readStringNullSafe(source);
    mNumber = ParcelBundleUtils.readStringNullSafe(source);
}

And here are my helper functions:

public static void writeBoolean(Parcel p, boolean b)
{
    p.writeByte((byte) (b ? 1 : 0));
}

public static boolean readBoolean(Parcel p)
{
    return p.readByte() == 1;
}

public static void writeStringNullSafe(Parcel p, String s)
{
    p.writeByte((byte) (s != null ? 1 : 0));
    if (s != null)
        p.writeString(s);
}

public static void writeIntegerNullSafe(Parcel p, Integer i)
{
    p.writeByte((byte) (i != null ? 1 : 0));
    if (i != null)
        p.writeInt(i);
}

public static void writeLongNullSafe(Parcel p, Long l)
{
    p.writeByte((byte) (l != null ? 1 : 0));
    if (l != null)
        p.writeLong(l);
}

public static void writeDoubleNullSafe(Parcel p, Double d)
{
    p.writeByte((byte) (d != null ? 1 : 0));
    if (d != null)
        p.writeDouble(d);
}

public static void writeParcelableNullSafe(Parcel p, Parcelable d, int flags)
{
    p.writeByte((byte) (d != null ? 1 : 0));
    if (d != null)
        p.writeParcelable(d, flags);
}

public static String readStringNullSafe(Parcel p)
{
    boolean isPresent = p.readByte() == 1;
    return isPresent ? p.readString() : null;
}

public static Integer readIntegerNullSafe(Parcel p)
{
    boolean isPresent = p.readByte() == 1;
    return isPresent ? p.readInt() : null;
}

public static Long readLongNullSafe(Parcel p)
{
    boolean isPresent = p.readByte() == 1;
    return isPresent ? p.readLong() : null;
}

public static Double readDoubleNullSafe(Parcel p)
{
    boolean isPresent = p.readByte() == 1;
    return isPresent ? p.readDouble() : null;
}

@SuppressWarnings("unchecked")
public static <T extends Parcelable> T readParcelableNullSafe(Parcel p, ClassLoader classLoader)
{
    boolean isPresent = p.readByte() == 1;
    return isPresent ? (T) p.readParcelable(classLoader) : null;
}
prom85
  • 16,896
  • 17
  • 122
  • 242

2 Answers2

1
int count = source.readInt(); // index is raised + 1
for (int i = 0; i < count; i++)
    mMatchesDataLoaded.add(ContactType.values()[source.readInt()]);  // index is raised by 1, starting with 1!

you loop from 0 to 4, but source.readInt() was already called once, so you called it 5 times in total.

dube
  • 4,898
  • 2
  • 23
  • 41
  • readInt() works like an iterator... It moves the internal pointer to the next position... so that is correct. And I don't loop from one to 4 but from 0 to some value between 0 and 4... – prom85 Oct 08 '14 at 09:47
  • ah, you lead me to the solution there! – dube Oct 08 '14 at 09:51
  • better suggestion now, still wrong: look at my save function, I write the size of the set into the Parcel before writing the set's data into it... I anyway need that to determine, how many entries a can read from the bundle – prom85 Oct 08 '14 at 09:58
0

ContactType contains 5 values, from index 0 to index 4. You are trying to access the index 5, which does not exist.

 mMatchesDataLoaded.add(ContactType.values()[source.readInt()]); 

source.readInt() gives you a 5, try to figure out, with debug, why does it contain this value.

My guess is that writeToParcel writes this 5, try to inspect mMatchesDataLoaded which maybe contains some additional unwanted data.

ToYonos
  • 16,469
  • 2
  • 54
  • 70
  • actually, that is quite apperent... I can't reproduce the case where it crashes... So I don't know, HOW the 5 comes in there. As I look through the code, it should not be there... – prom85 Oct 08 '14 at 09:22
  • You can't reproduce the case so the issue comes from one of your users ? – ToYonos Oct 08 '14 at 09:29
  • I hate when I can't reproduce an issue that's coming from a user ;) No choice then, ask a specific user who's impacted, ask him about what he did, maybe a special configuration or something like that. – ToYonos Oct 08 '14 at 09:49
  • for now, I changed my functions to use serialisation... maybe this leads to another exception (but probably it won't)... Actually, I will have to do something like you proposed... But I will probably log all data before writing and when reading... – prom85 Oct 08 '14 at 09:54