Search code examples
androidparcelable

Parcelable get huge infinite items from array up to OOM


I am using Parcelable to communicate between fragments. Everything is working good, but sometimes when Android kills app process to free up the memory and user returns to app, the Parcelable gets huge amount of items from arrays like couple millions items instead of 2-3 and of course it throws OOM. I suppose something's wrong with CREATOR, but I can't handle it. The code for parcelable is composed from super class and child class and my interested item class:

base class:

 public abstract class BaseRs implements Parcelable {

private String token;
private String msgAlert;
private String simpleMsg;
private AdsObj adsObj;    
private Map<String, String> mapSettings;

//getters & setters

public BaseRs() {
}


protected BaseRs(Parcel in) {
    token = in.readString();
    msgAlert = in.readString();
    simpleMsg = in.readString();
    adsObj = in.readParcelable(AdsObj.class.getClassLoader());
    mapSettings = MapParcelable.readParcelable(in);
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(token);
    dest.writeString(msgAlert);
    dest.writeString(simpleMsg);
    dest.writeParcelable(adsObj, flags);
    MapParcelable.writeToParcel(dest, mapSettings);
 }

}

child class

public class GetSalesItemsRs extends BaseRs {

private SaleCoinItem[] coinPacksArray;

//getters+ setters


protected GetSalesItemsRs(Parcel in) {
    super(in);
    coinPacksArray = in.createTypedArray(SaleCoinItem.CREATOR);
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    super.writeToParcel(dest, flags);
    dest.writeTypedArray(coinPacksArray, flags);
}

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

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

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

and my interested object

public class SaleCoinItem implements Parcelable {

private int amount;
private double price;
private int sortOrder;

//getters & setters

protected SaleCoinItem(Parcel in) {
    amount = in.readInt();
    price = in.readDouble();
    sortOrder = in.readInt();
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(amount);
    dest.writeDouble(price);
    dest.writeInt(sortOrder);
}

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

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

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


       }
    };
}

The functiona to parcel Map - maybe here is the error ;)

public class MapParcelable {

public static void writeToParcel(Parcel out, Map<String, String> map) {
    if (map != null) {
        out.writeInt(map.size());
        for (Map.Entry<String, String> entry : map.entrySet()) {
            out.writeString(entry.getKey());
            out.writeString(entry.getValue());
        }
    }
}

public static Map<String, String> readParcelable(Parcel parcel) {
    Map<String, String> map = new HashMap<String, String>();
    int size = parcel.readInt();
    for (int i = 0; i < size; i++) {
        String key = parcel.readString();
        String value = parcel.readString();
        map.put(key, value);
    }

    return map;


   }

}

I get parcelable obj inside the onCreateView method :

GetSalesItemsRs mGetSalesItemsRs = getArguments().getParcelable(KEY_PARCEL);

Solution

  • I think the problem is that if the Map is null you're not writing the size field to the Parcel, but when you read it you always read the size field, and it might end up reading some random value. So or you change the code to

    if (map != null) {
        out.writeInt(map.size());
        for (Map.Entry<String, String> entry : map.entrySet()) {
            out.writeString(entry.getKey());
            out.writeString(entry.getValue());
        }
    }else{
        out.writeInt(0);
    }
    

    and you will never get a null value when recreating the class, or you create another boolean field "hasMap" and always write that one