3

I am trying to understand how to read null values from Parcel properly. Let's look my use case:

public class MyModel implements Parcelable {
    private Long id;
    private String name;

    public MyModel() {}

    public MyModel(Parcel in) {
        readFromParcel(in);
    }

    @Override public int describeContents() {
        return 0;
    }

    @Override public void writeToParcel(Parcel dest, int flags) {
        dest.writeValue(id);
        dest.writeValue(name);
    }

    public void readFromParcel(Parcel in) {
        id = (Long) in.readValue(Long.class.getClassLoader());
        name = (String) in.readValue(String.class.getClassLoader());
    }
}

For the sake of this question, let's take an example of field name which is of String type.

I understand I can use writeString instead of writeValue, but writeString will throw NullPointerException if name was null.

So now the real issue is, if name was null and i attempt to readValue and then casting null to String will throw an Exception.

What is the best approach to read null values from Parcel in that case? Do I need to check for null every time I attempt to read from Parcel? But that will be too much if you have a lot of fields in a model. Is there a difference between passing null or a ClassLoader when reading? Does a class loader returns an empty/default object if the value is null when reading Parcel?

The error I usually get is this, which happens when it is reading one of the Strings from Parcel:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.company.MyActivity}: java.lang.RuntimeException: Parcel android.os.Parcel@42b82908: Unmarshalling unknown type code 7209045 at offset 1368

SherCoder
  • 678
  • 2
  • 11
  • 26
  • 2
    writeString(null) will work, see how writeValue is implemented: http://androidxref.com/4.4_r1/xref/frameworks/base/core/java/android/os/Parcel.java#1182 it just uses writeString with the passed argument – pskink Oct 07 '14 at 04:16
  • @pskink I see that writeString calls nativeWriteString, do you know where those are implemented? – SherCoder Oct 07 '14 at 16:54
  • 1
    why do you want to know this? its perfectly OK to write null String, try to call writeString(null) and it will work, btw its here: http://androidxref.com/4.4_r1/xref/frameworks/base/core/jni/android_os_Parcel.cpp#641 – pskink Oct 07 '14 at 17:07
  • @pskink I was just curious. Thanks. – SherCoder Oct 07 '14 at 17:44
  • @pskink I have a related question about writing `Long`, not primitive but the object `Long`. if it is null at time of writing, what will it be when I am reading? Because readLong returns `long` not `Long`, so will that `long` be a random value? – SherCoder Oct 07 '14 at 17:45
  • then use: Long l = (Long) parcel.readValue(Long.class.getClassLoader()); – pskink Oct 07 '14 at 18:10
  • @pskink but if readValue returned null then casting will fail and throw exception, right? Or does ClassLoader prevents that? I guess I don't understand ClassLoader very well. – SherCoder Oct 07 '14 at 18:58
  • i dont understand what you really want – pskink Oct 07 '14 at 18:59
  • 1
    @pskink Actually just confirmed that writeLong will throw NullPointerException if Long is null. – SherCoder Oct 07 '14 at 19:07
  • then use writeValue if you need null Long – pskink Oct 07 '14 at 19:16
  • @pskink Thanks. Sorry about these extended questions. Just trying to understand the Parcel. Appreciate your time. – SherCoder Oct 07 '14 at 19:17
  • What you say here is just **not true**: "writeString will throw NullPointerException if name was null" See [my answer below](http://stackoverflow.com/a/34370801/383414) and [this related question](http://stackoverflow.com/a/34370623/383414). – Richard Le Mesurier Dec 19 '15 at 12:40
  • @SherCoder `writeLong(long)` expects a primitive `long` value, rather than a `Long` object. The npe you get is from Java autoboxing your `null` into a `long` before it gets passed into the method. That is why you can't do that. With a `String` object it is different, since the `writeString(String)` method expects an object reference, not a primitive. – Richard Le Mesurier Dec 19 '15 at 12:46

3 Answers3

1

How about supply ClassLoader with MyModel.class. getClassLoader()?

Feng Dai
  • 633
  • 5
  • 9
  • An example would help improve this answer, but there's no reason to flag it. If you don't like it, down vote it. – ArtOfWarfare Oct 07 '14 at 02:14
  • Clicked "flag" by mistake. – Feng Dai Oct 07 '14 at 03:48
  • How would that help? Explanation would be helpful. Thanks. – SherCoder Oct 07 '14 at 21:08
  • I wrote a class the same as yours. Then I kept the ```name``` field ```null```, and passed it from ActivityA to ActivityB. When I attempted to get it out of the Intent inside ActivityB, no exception was thrown. Is there any other condition for this kind of exception to occur? – Feng Dai Oct 08 '14 at 01:56
1

What is the best approach to read null values from Parcel in that case?

You can use the string-specific methods:

  • Parcel.writeString(String)
  • Parcel.readString()

They work perfectly well with null values.


For example, assume you have a class with the following fields in it:

public class Test implements Parcelable {
    public final int id;
    private final String name;
    public final String description;
    ...

You create the Parcelable implementation like this (using Android Studio autoparcelable tool):

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(id);
    dest.writeString(null); // NOTE the null value here
    dest.writeString(description);
}

protected Test(Parcel in) {
    id = in.readInt();
    name = in.readString();
    description = in.readString();
}

Your Test.name value will be deserialised as null correctly, without requiring any casting to String.

Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
-1

I think the best way to do this is to write the length of string name before name. If name is null, just write 0 as its length, and do not write name. When reading, if length is read and its value is zero, do not read name and just set name to null.

Byungjoon Lee
  • 913
  • 6
  • 18
  • This is interesting but if I have a lot of String fields in my model, then I will have to do the same for every single one of them. – SherCoder Oct 07 '14 at 21:09