5

My class ExpenseFB, which implements Parcelable, contains a Map of UserFB (which implements Parcelable too):

ExpenseFB:

public class ExpenseFB implements Parcelable {

private String id;
private String name;
private String description;
private String whopaidID;
private String whopaidName;
private Double amount;
private Map<String, UserFB> partecipants;
// setters and getters...
@Override
public int describeContents() {
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(id);
    dest.writeString(name);
    dest.writeString(description);
    dest.writeString(whopaidID);
    dest.writeString(whopaidName);
    dest.writeMap(partecipants);
}


protected ExpenseFB(Parcel in) {
    id = in.readString();
    name = in.readString();
    description = in.readString();
    whopaidID = in.readString();
    whopaidName = in.readString();
    in.readMap(partecipants,UserFB.class.getClassLoader());
}

public static final Creator<ExpenseFB> CREATOR = new Creator<ExpenseFB>() {
    @Override
    public ExpenseFB createFromParcel(Parcel in) {
        return new ExpenseFB(in);
    }

    @Override
    public ExpenseFB[] newArray(int size) {
        return new ExpenseFB[size];
    }
};
}

UserFB:

public class UserFB implements Parcelable{

private String id;
private String name;
private String email;
private Map<String, GroupFB> groups;
private Map<String, UserFB> friends;
// setters and getters
@Override
public int describeContents() {
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(id);
    dest.writeString(name);
    dest.writeString(email);
    dest.writeMap(groups);
    dest.writeMap(friends);
}

protected UserFB(Parcel in) {
    id = in.readString();
    name = in.readString();
    email = in.readString();
    in.readMap(groups,GroupFB.class.getClassLoader());
    in.readMap(friends,UserFB.class.getClassLoader());
}

public static final Creator<UserFB> CREATOR = new Creator<UserFB>() {
    @Override
    public UserFB createFromParcel(Parcel in) {
        return new UserFB(in);
    }

    @Override
    public UserFB[] newArray(int size) {
        return new UserFB[size];
    }
};
}

I want to pass an ExpenseFB object between two Activities by adding the object ExpenseFB to the intent:

intent.putExtra("id", expenseFB);

When, in debug mode, I execute getIntent().getParcelableExtra("id") in the second activity it raises the following exception when tries to do the readMap() method on the partecipants map:

 ... Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.Map.put(java.lang.Object, java.lang.Object)' on a null object reference

I see that the partecipants map in the first activity is filled: I think that the problem is in the writeMap() method.
Does exist a standard or better way to pass a Parcelable object containing a Map?
Have I to call another method to parcel the Map?

I don't want to use Serializable object because I read that they make worse performances.

A. Wolf
  • 1,309
  • 17
  • 39

2 Answers2

9

The problem is that readMap() is used to read data from a Parcel into and existing Map. You haven't created the Map before calling readMap(), so you get the NullPointerException.

You can solve this by initializing the map when you declare it:

private Map<String, GroupFB> groups = new HashMap<String, GroupFB>();
private Map<String, UserFB> friends = new HashMap<String, UserFB>();

Or, you can create the empty Map in the UserFB constructor, like this:

protected UserFB(Parcel in) {
    id = in.readString();
    name = in.readString();
    email = in.readString();
    groups = new HashMap<String, GroupFB>();
    in.readMap(groups,GroupFB.class.getClassLoader());
    friends = new HashMap<String, UserFB>()
    in.readMap(friends,UserFB.class.getClassLoader());
}
David Wasser
  • 93,459
  • 16
  • 209
  • 274
1

You got the point but I think you need to know how to write Map<> into parcelable

Pasting writeParcel() method

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(this.groups.size());
    for (Map.Entry<String, GroupFb> entry : this.groups.entrySet()) {
        dest.writeString(entry.getKey());
        dest.writeParcelable(entry.getValue(), flags);
    }
}

protected UserFB (Parcel in) {
    int groupsSize = in.readInt();
    this.groups = new HashMap<String, GroupFb>(groupsSize);
    for (int i = 0; i < groupsSize; i++) {
        String key = in.readString();
        GroupFb value = in.readParcelable(GroupFb.class.getClassLoader());
        this.groups.put(key, value);
    }
}

Do the same for another Map<> too.

Paresh P.
  • 6,677
  • 1
  • 14
  • 26
  • 1
    The `Parcel` class already has a `writeMap()` and `readMap()` method that do exactly this. Why would you not use the existing method? There is no reason to reinvent the wheel here. – David Wasser Apr 18 '17 at 17:09
  • 2
    It is not recommended to use wirteMap(). It is written in this method javadoc. Also it will only cover situation with String keys in map. So this answer is more general and correct. – Taras Lozovyi Oct 23 '18 at 10:38