How to serialize null value when using Parcelable interface

29,105

Solution 1

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

Solution 2

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.

Solution 3

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;
}

Solution 4

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).

Share:
29,105
igor.beslic
Author by

igor.beslic

Updated on July 08, 2022

Comments

  • igor.beslic
    igor.beslic almost 2 years

    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!

  • Sofi Software LLC
    Sofi Software LLC over 11 years
    and to read the value back... s = (String) in.readValue(String.class.getClassLoader());
  • Matt
    Matt about 11 years
    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;"
  • Price
    Price almost 10 years
    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.
  • miguel
    miguel about 9 years
    @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. github.com/android/platform_frameworks_base/blob/…
  • Admin
    Admin about 6 years
    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.
  • meszias
    meszias over 5 years
    You could write the code for answering the part of reading the object.
  • arenaq
    arenaq about 5 years
    Yep, serializable with cast is working like a charm.
  • ArteFact
    ArteFact about 5 years
    Should be accepted answer, with @SofiSoftwareLLC 's comment integration