How to serialize null value when using Parcelable

2019-01-17 09:37发布

问题:

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!

回答1:

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



回答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.



回答3:

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



回答4:

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