johncarl81 / parceler

:package: Android Parcelables made easy through code generation.
http://parceler.org
Apache License 2.0
3.56k stars 273 forks source link

How to parcel java.lang.Class objects in a generic way? #374

Open StephanSchuster opened 5 years ago

StephanSchuster commented 5 years ago

I have the following generic Data container:

@Parcel
public class Data implements Cloneable {

    @ParcelPropertyConverter(ValuesParcelConverter.class)
    SparseArray<Object> mValues;

    ...
}

The ValuesParcelConverter does this:

        public void toParcel(SparseArray<Object> values, android.os.Parcel parcel) {
            if (values == null) {
                parcel.writeInt(-1);
            } else {
                int size = values.size();
                parcel.writeInt(size);
                for (int i = 0; i < size; ++i) {
                    int id = values.keyAt(i);
                    Object value = values.valueAt(i);
                    parcel.writeInt(id);
                    try {
                        parcel.writeParcelable(Parcels.wrap(value), 0);
                    } catch (Exception e) {
                        ...
                    }
                }
            }
        }

Problem: If a value is a class object (e.g. MyClass.class) it of course cannot be serialized. Question: What's the best approach to enable this for my generic Data container?

Option 1: Bad but works: Check each value before wrapping if its an instance of Class and if so, serialize the class name to string via Class.getName() and prefix it with some magic string. On deserializing check each value if it's an instance of String and starts with the magic prefix. If yes strip the prefix and use Class.forName(). While the serializing mechanism (Class -> String -> Class) is okay, I don't wanna check this for each value because of performance. In worst case none of 1000 values is a class object. And I do NOT know the values beforehand.

Option 2: Hopefully better but not working:

@ParcelClass(
        value = Class.class,
        annotation = @Parcel(converter = MyClassConverter.class))
public class App extends BaseApp {

MyClassConverter is doing the serialization via Class.getName() and Class.forName(). But this results in the following error:

Unable to find generated Parcelable class for java.lang.Class, verify that your class is configured properly and that the Parcelable class java.lang.Class$$Parcelable is generated by Parceler.

Looking at the generated source I see the following Class: java_.lang.Class$$Parcelable Is this underscore on purpose? Is this why it's failing? Is this approach even valid? Did I misunderstand something here?

Any help would be appreciated.