65

regarding my code example down, what shold I do if one Locable's variables is null? In example, now if l.getZoom() returns null, I got NullPointerException.

@Override
public void writeToParcel(Parcel parcel, int arg1) {
    parcel.writeInt(count);
    for(Locable l:locableArr){
        parcel.writeInt(l.getOriginId());
        parcel.writeInt(l.getLocableType());
        parcel.writeInt(l.getZoom());
        parcel.writeDouble(l.getLatituda());
        parcel.writeDouble(l.getLongituda());
        parcel.writeString(l.getTitle());
        parcel.writeString(l.getSnipet());
    }

}

Thanks!

igor.beslic
  • 954
  • 2
  • 9
  • 16
  • It seems NPE is result of Auto-unboxing. Probably I should just check for null value and set it to 0? – igor.beslic May 05 '11 at 23:26
  • If zoom has values 0+, consider representing null as -1 – JAL May 05 '11 at 23:29
  • For now I'm doing similar thing, if values are NULL I'm writing some values such -1 or empty string which I'm during deserialization setting to NULL. Ugly... I would like if somebody can confirm this is right way I would be more satisfied with my code. – igor.beslic May 25 '11 at 10:10
  • well primitives have no concept of null (or objective C's nil) As a generic solution I wonder if you can send pairs of primitives to represent an object say Int for int, first a boolean isNull and then the primitive int allowing you to create Integer(int) or Integer(null). – JAL May 25 '11 at 22:17

4 Answers4

114

You can use Parcel.writeValue for marshalling generic object with null value.

artch
  • 4,487
  • 2
  • 28
  • 35
  • 53
    and to read the value back... s = (String) in.readValue(String.class.getClassLoader()); – Sofi Software LLC Jan 10 '13 at 20:08
  • 2
    @SofiSoftwareLLC looking at the source code of parcel.readValue it only needs a class loader for types Map, Parcelable, ArrayList, Object[] and Parcelable[]. For other types you can pass in null for the classloader. https://github.com/android/platform_frameworks_base/blob/3bdbf644d61f46b531838558fabbd5b990fc4913/core/java/android/os/Parcel.java#L2002 – miguel Feb 04 '15 at 18:52
  • Should be accepted answer, with @SofiSoftwareLLC 's comment integration – ArteFact Mar 22 '19 at 13:52
26

I'm using a Parcelable class that has Integer and Boolean fields as well, and those fields can be null.

I had trouble using the generic Parcel.writeValue method, particularly when I was trying to read it back out via Parcel.readValue. I kept getting a runtime exception that said it couldn't figure out the type of the parceled object.

Ultimately, I was able to solve the problem by using Parcel.writeSerializable and Parcel.readSerializable with a type cast, as both Integer and Boolean implement the Serializable interface. The read and write methods handle null values for you.

Justin Hong
  • 373
  • 3
  • 8
16

This is the solution I came up with to write strings safely:

private void writeStringToParcel(Parcel p, String s) {
    p.writeByte((byte)(s != null ? 1 : 0));
    p.writeString(s);
}

private String readStringFromParcel(Parcel p) {
    boolean isPresent = p.readByte() == 1;
    return isPresent ? p.readString() : null;
}
Felipe Lima
  • 10,530
  • 4
  • 41
  • 39
  • I had some trouble with this after i copied and pasted your code, not realizing that in readStringFromParcel() i had to cast the 1 to byte before comparing it to p.readByte(). In the end I chose to "p.writeInt((s != null ? 1 : 0));" and "boolean isPresent = p.readInt() == 1;" – Matt Apr 21 '13 at 17:53
  • 3
    You should write the string using the line `p.writeString(s)` only when it is not null; otherwise you will get a NullPointerException at this line. – Price Jul 18 '14 at 06:13
  • If you are using `p.writeParcelable` instead of `p.writeString` you won't get a NullPointerException immediately, just some shady ClassCastException later on caused by the misaligned read and write sequences. You should only write the object if it is not null. – Roland Feb 21 '18 at 16:21
14

Most serialization code that I've seen uses either flags to indicate the presence/absence of a value OR precedes the value with a count field (for example, when writing arrays) where the count field is just set to zero if the value doesn't exist at all.

Examining the source code of Android core classes reveals code like this (from Message class):

    if (obj != null) {
        try {
            Parcelable p = (Parcelable)obj;
            dest.writeInt(1);
            dest.writeParcelable(p, flags);
        } catch (ClassCastException e) {
            throw new RuntimeException(
                "Can't marshal non-Parcelable objects across processes.");
        }
    } else {
        dest.writeInt(0);
    }

or this (from Intent class):

    if (mCategories != null) {
        out.writeInt(mCategories.size());
        for (String category : mCategories) {
            out.writeString(category);
        }
    } else {
        out.writeInt(0);
    }

My suggestion: In your code, if there is no functional difference between "zoom == null" and "zoom == 0", then I would just declare zoom as a primitive (int instead of Integer) OR initialize it to zero in the constructor and ensure that you never set it to null (then you can be guaranteed that it will never be null and you won't have to add special code to deal with that in your serialization/deserialization methods).

David Wasser
  • 93,459
  • 16
  • 209
  • 274