Parceler unwrap giving wrong data

1.1k views Asked by At

I'm using parceler library to implement Parcelable interface.

I have such model

@Parcel(Parcel.Serialization.BEAN)
public class Ads {
private Long id;
private String title;
private String description;
private AdsType adsType;
private String phone;
private String email;
private String city;
private Long categoryId;
private ArrayList<Integer> creationDate;
//TODO remove transient
private transient ArrayList<Long> imageIds;
private transient Long price;


@SerializedName("adsCategory")
private AdvCategory advCategory;

public Ads() {}

public Ads(String title, String description, AdsType adsType, String
        phone, String email, String city, Long categoryId, Long price, ArrayList<Long> imageIds) {
    this.title = title;
    this.description = description;
    this.adsType = adsType;
    this.phone = phone;
    this.email = email;
    this.city = city;
    this.categoryId = categoryId;
    this.price = price;
    this.imageIds = imageIds;
}

@ParcelConstructor
public Ads(Long id, String title, String description, AdsType adsType,
           String phone, String email, String city, ArrayList<Long>
                   imageIds, Long price, ArrayList<Integer> creationDate, AdvCategory advCategory) {
    this.id = id;
    this.title = title;
    this.description = description;
    this.adsType = adsType;
    this.phone = phone;
    this.email = email;
    this.city = city;
    this.imageIds = imageIds;
    this.price = price;
    this.creationDate = creationDate;
    this.advCategory = advCategory;
}

public Long getId() {
    return id;
}

public String getTitle() {
    return title;
}

public String getDescription() {
    return description;
}

public AdsType getAdsType() {
    return adsType;
}

public String getPhone() {
    return phone;
}

public String getEmail() {
    return email;
}

public String getCity() {
    return city;
}

public AdvCategory getAdvCategory() {
    return advCategory;
}

public void setAdvCategory(AdvCategory advCategory) {
    this.advCategory = advCategory;
}

public Long getCategoryId() {
    return categoryId;
}

public ArrayList<Long> getImageIds() {
    return imageIds;
}

public void setImageIds(ArrayList<Long> imageIds) {
    this.imageIds = imageIds;
}

public int getPrice() {
    //TODO replace with real price
    return new Random().nextInt(100000);
}

public void setPrice(Long price) {
    this.price = price;
}

public ArrayList<Integer> getCreationDate() {
    return creationDate;
}

public void setCreationDate(ArrayList<Integer> creationDate) {
    this.creationDate = creationDate;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Ads ads = (Ads) o;
    return id.equals(ads.id);
}

@Override
public int hashCode() {
    int result = id.hashCode();
    result = 31 * result + title.hashCode();
    result = 31 * result + description.hashCode();
    result = 31 * result + adsType.hashCode();
    result = 31 * result + (phone != null ? phone.hashCode() : 0);
    result = 31 * result + (email != null ? email.hashCode() : 0);
    result = 31 * result + (city != null ? city.hashCode() : 0);
    result = 31 * result + advCategory.hashCode();
    result = 31 * result + (categoryId != null ? categoryId.hashCode() : 0);
    return result;
}

@Override
public String toString() {
    return "Ads{" +
            "id=" + id +
            ", title='" + title + '\'' +
            ", description='" + description + '\'' +
            ", adsType=" + adsType +
            ", phone='" + phone + '\'' +
            ", email='" + email + '\'' +
            ", city='" + city + '\'' +
            ", creationDate='" + creationDate.toString() +
            '}';
}

public static class List extends ArrayList<Ads> {}
}

I'm wraping my model and puting it into intent.

 Intent adsDetailsIntent = new Intent(this, AdsDetailsActivity.class);
    Bundle details = new Bundle();
    Ads advertisement = mAdsAdapter.getItem(position);
    details.putParcelable(AdsDetailsActivity.ADS_DETAILS, Parcels.wrap(advertisement));
    Ads ads = Parcels.unwrap(details.getParcelable(AdsDetailsActivity.ADS_DETAILS));
    Log.d("ads", ads.toString());
    adsDetailsIntent.putExtras(details);
    startActivity(adsDetailsIntent);

And unwrapping in activity

mAdsDetails = Parcels.unwrap(
            (Parcelable) this.getIntent().getParcelableExtra(ADS_DETAILS));

but sometimes field "creationDate" has wrong value after unwrapping in activity.

I tried to log it, and after unwrapping from Bundle - it's ok, but in activity - it has weird data.

Example :

unwrap from bundle immediately after creating it

Ads{id=16, title='Mtitle', description='Mads', adsType=BUY, phone='+30890931231', email='+380932309046', city='Анабарский национальный улус', creationDate='[2015, 8, 8, 9, 27, 0, 350946000]}

unwrap from activity intent.getExtra()

Ads{id=null, title='null', description='null', adsType=null, phone='null', email='null', city='null', creationDate='[8, 8, 9, 27, 0, 350946000, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

I don't know why, but it's creating an array with creationDate1 size and filling it with zeros.

2

There are 2 answers

2
t0mm13b On

I noticed that in your unwrapping the class

mAdsDetails = Parcels.unwrap(
            (Parcelable) this.getIntent().getParcelableExtra(ADS_DETAILS));

Have you tried it this way:

mAdsDetails = Parcels.unwrap(this.getIntent().getExtras().get(ADS_DETAILS));
0
Del On

I believe this could be related to the discrepancy between the int return type of the #getPrice function and the Long type of the price argument to the constructor annotated with @ParcelConstructor.

This will result in generated code that:

  1. writes an integer into the parcel for serialization
  2. attempts to read a complex, larger type upon deserialization
  3. deserialization continues, but effectively reading garbage since the readFoo methods are called with the "wrong" offset in the buffer

Specifically, when inspecting the code generated by Parceler and debugging the same issue in my code I found that:

  • primitive types are written directly into the parcel as a single value
  • complex types (like the wrappers for primitive types) are written with two values: one for null vs. non-null as a flag, and then possibly the actual value if not null

See this issue I made for the Parceler project:

https://github.com/johncarl81/parceler/issues/234