Can auto-value-parcel-adapter cope with Typed Set of another auto-value Class?
Asked Answered
M

2

8

I am investigating auto-value and its extensions, namely auto-value-parcel and auto-value-parcel-adapter within my Android application.

I have these model classes:-

@AutoValue
public abstract class Xenarchaeota implements Parcelable {

    @ParcelAdapter(AmoebaTypeAdapter.class)
    public abstract Set<Amoeba> amoebas();

    public static Builder builder() {
        return new AutoValue_Xenarchaeota.Builder();
    }

    @AutoValue.Builder
    public abstract static class Builder {

        public abstract Builder setAmoebas(Set<Amoeba> value);

        public abstract Xenarchaeota build();
    }
}

and

@AutoValue
public abstract class Amoeba implements Parcelable {

    public abstract String surname();

    public static Builder builder() {
        return new AutoValue_Amoeba.Builder();
    }

    @AutoValue.Builder
    public abstract static class Builder {

        public abstract Builder surname(final String value);

        public abstract Amoeba build();
    }

}

My type adapter is where my issues arise

class AmoebaTypeAdapter implements TypeAdapter<Set<Amoeba>> {

    @Override
    public Set<Amoeba> fromParcel(Parcel in) {
        final ArrayList<Amoeba> arrayList = new ArrayList<>();
        in.readTypedList(arrayList, Amoeba.CREATOR); // How to access the CREATOR?
        return new TreeSet<>(arrayList);
    }

    @Override
    public void toParcel(Set<Amoeba> value, Parcel dest) {
        final ArrayList<Amoeba> arrayList = new ArrayList<>(value);
        dest.writeTypedList(arrayList);
    }
}

The CREATOR I need to pass to the readTypedArray is declared in AutoValue_Amoeba.

Where is my mistake? misunderstanding of auto-value-parcel?

Madox answered 19/1, 2018 at 15:58 Comment(0)
A
3

AutoValue: Parcel extension can't handle sets, but if you convert the property to a List things will work out of the box without a custom adapter. If you want to treat it as a Set you could do this. Keep in mind you'll probably also want to cache the Set.

@AutoValue
public abstract class Xenarchaeota implements Parcelable {

    abstract List<Amoeba> amoebaList();
    public Set<Amoeba> amoebas() {
        return new TreeSet(amoebaList());
    }

    public static Builder builder() {
        return new AutoValue_Xenarchaeota.Builder();
    }

    @AutoValue.Builder
    public abstract static class Builder {

        public abstract Builder setAmoebas(Set<Amoeba> value);

        public abstract Xenarchaeota build();
    }
}
Ardatharde answered 27/1, 2018 at 20:13 Comment(3)
thanks for looking at this. I am converting my Set to a List in my adapter however I thought I had to use a typed list and pass the Type CREATOR.Madox
it wasnt the Set "issue" more saving Amoeba in Parcelable without using a TypedList. I thought that by not using a TypedList it increases the size of the Parcelable and gives poor performance when compared to Parceling a TypedList.Madox
If you make it a List, it will make a round trip through Parcel#writeValue(val), so there will be a perf hit there for some extra method calls and type checks. I'd consider that a small hit since this is serialization, which involves other perf issues (IPC, etc), but that's up to you. Another option would be making it an array, and wrapping it like detailed here. Parcelable[] gets directly written, but reading requires a class loader of the class to be read (it uses reflection to load the Creator).Ardatharde
M
2

I believe I have identified a solution as follows:-

Currently the auto-value-parcel class

com.ryanharter.auto.value.parcel.AutoValueParcelExtension

has a method called generateCreator:-

FieldSpec generateCreator(ProcessingEnvironment env, TypeName autoValueType, List<Property> properties, TypeName type, Map<TypeMirror, FieldSpec> typeAdapters) {
        ClassName creator = ClassName.bestGuess("android.os.Parcelable.Creator");

        TypeName creatorOfClass = ParameterizedTypeName.get(creator, type);
        ...
        ...

This method generates a Parcelable CREATOR that resembles this

public static final Parcelable.Creator<AutoValue_Amoeba> CREATOR = new Parcelable.Creator<AutoValue_Amoeba>() {
    @Override
    public AutoValue_Amoeba createFromParcel(Parcel in) {
      return new AutoValue_Amoeba(
          in.readString()
      );
    }
    @Override
    public AutoValue_Amoeba[] newArray(int size) {
      return new AutoValue_Amoeba[size];
    }
  };

If the generateCreator method was changed as follows:-

FieldSpec generateCreator(ProcessingEnvironment env, TypeName autoValueType, List<Property> properties, TypeName type, Map<TypeMirror, FieldSpec> typeAdapters) {
        ClassName creator = ClassName.bestGuess("android.os.Parcelable.Creator");

        TypeName creatorOfClass = ParameterizedTypeName.get(creator, autoValueType);  // CHANGE MADE HERE!!! swap type with autoValueType
        ...
        ...

This method would then generate a Parcelable CREATOR that resembles this

public static final Parcelable.Creator<Amoeba> CREATOR = new Parcelable.Creator<Amoeba>() {
    @Override
    public AutoValue_Amoeba createFromParcel(Parcel in) {
      return new AutoValue_Amoeba(
          in.readString()
      );
    }
    @Override
    public AutoValue_Amoeba[] newArray(int size) {
      return new AutoValue_Amoeba[size];
    }
  };

This CREATOR now allows a TypeAdapter to employ the CREATOR as shown here

class AmoebaTypeAdapter implements TypeAdapter<Set<Amoeba>> {

    @Override
    public Set<Amoeba> fromParcel(Parcel in) {
        final List<Amoeba> arrayList = new ArrayList<>();
        in.readTypedList(arrayList, AutoValue_Amoeba.CREATOR);
        return new TreeSet<>(arrayList);
    }

    @Override
    public void toParcel(Set<Amoeba> value, Parcel dest) {
        final ArrayList<Amoeba> arrayList = new ArrayList<>(value);
        dest.writeTypedList(arrayList);
    }
}
Madox answered 22/1, 2018 at 11:50 Comment(1)
We looked into this early on and had some discussion here: github.com/rharter/auto-value-parcel/issues/101Ardatharde

© 2022 - 2024 — McMap. All rights reserved.