Using Parcelable to pass highly nested classes bet

2019-07-05 05:23发布

问题:

Suppose I want to store a custom object of type MyObject in an Intent. The way to do this is to make MyObject implement Parcelable. If one of the fields of MyObject is also a custom object of type Widget the obvious thing to do is to make Widget implement Parcelable too.

The trouble is that there is a huge amount of boilerplate involved when implementing Parcelable. You can get around this by not making Widget implement Parcelable but instead just giving it a constructor taking a Parcel and a method writeToParcel as follows:

public final class Widget {

    private final int a;
    private final String b;

    Widget(Parcel in) {
        a = in.readInt();
        b = in.readString();
    }

    void writeToParcel(Parcel out) {
        out.writeInt(a);
        out.writeString(b);
    }
}

You can then have a Widget field in a Parcelable object as follows:

public class MyObject implements Parcelable {

    private final int x;
    private final Widget w;

    MyObject(int x, Widget w) {
        this.x = x;
        this.w = w;
    }

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

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(x);
        w.writeToParcel(out);
    }

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

Is this an acceptable approach? Is it considered unidiomatic android to have many custom classes in a project that can be written to and read from Parcels without them actually implementing Parcelable? Or does the fact that I am using a Parcelable to pass complex objects with many fields of custom types (which in turn have many fields of custom type etc etc), indicate that I shouldn't be using Parcelable in the first place?

回答1:

I would (and did) go with Parceler: https://github.com/johncarl81/parceler

Parceler is a code generation library that generates the Android Parcelable boilerplate source code. No longer do you have to implement the Parcelable interface, the writeToParcel() or createFromParcel() or the public static final CREATOR. You simply annotate a POJO with @Parcel and Parceler does the rest.

It's really easy to use.



回答2:

It is recommended to use Parcelable when dealing with passing custom Objects through intents in Android. There isn't an "easy" work around. Since you are dealing with just one extra level of a custom Object (Widget), I would recommend you make Widget Parcelable also. You can also check out this link to see why it is the better approach than using default Serialization. https://coderwall.com/p/vfbing/passing-objects-between-activities-in-android



回答3:

If your classes are beans, the best solution is the accepted one. If not, I have found that you can (slightly) reduce the pain of implementing Parcelable by creating abstract classes ParcelablePlus and CreatorPlus like this.

ParcelablePlus:

abstract class ParcelablePlus implements Parcelable {

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

CreatorPlus:

abstract class CreatorPlus<T extends Parcelable> implements Parcelable.Creator<T> {

    private final Class<T> clazz;

    CreatorPlus(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    @SuppressWarnings("unchecked")
    public final T[] newArray(int size) {
        // Safe as long as T is not a generic type.
        return (T[]) Array.newInstance(clazz, size);
    }
}

Then the Widget class becomes:

public final class Widget extends ParcelablePlus {

    private final int a;
    private final String b;

    Widget(int a, String b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(a);
        out.writeString(b);
    }

    public static final Creator<Widget> CREATOR = new CreatorPlus<Widget>(Widget.class) {
        @Override
        public Widget createFromParcel(Parcel in) {
            return new Widget(in.readInt(), in.readString());
        }
    };
}