Search code examples
javaandroidinheritanceconstructorparcelable

Problem with Constructor in Parcelable in both Abstract and Derived Classes


I have an abstract class and its children, and I wanted to pass the children through one activity to another through intent.putExtra(String, Parcelable); The activity receiving the Intent would accept any parcelable since it's an object extending the abstract class. I made an implementation of Parcelable both in the abstract class and the children:

public abstract class MyAbstractClass implements Parcelable{
private double dField;
private Integer stringResource;
private MyEnum objEnumType;

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

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        //TODO Age
        parcel.writeDouble(dField);        parcel.writeString(this.objEnumType.toString());
        parcel.writeInt(stringResource);
    }

    public static final Parcelable.Creator<MyAbstractClass> CREATOR = new Creator<MyAbstractClass>() {
        @Override
        public MyAbstractClass createFromParcel(Parcel parcel) {
            return null;
        }

        @Override
        public MyAbstractClass[] newArray(int i) {
            return new MyAbstractClass[0];
        }
    };

    protected MyAbstractClass(Parcel in){
       dField = in.readDouble();
       this.objEnumType = MyEnum.valueOf(in.readString());
       stringResource = in.readInt();
    }
}

In the child class:

public class MyChildClass extends MyAbstractClass{
    public int describeContents(){
        return 0;
    }

    public void writeToParcel(Parcel out, int flags){
        super.writeToParcel(out,flags);
    }

    public static final Parcelable.Creator<MyChildClass> CREATOR = new Creator<MyChildClass>() {
        @Override
        public MyChildClass createFromParcel(Parcel parcel) {
            return null;
        }

        @Override
        public MyChildClass[] newArray(int i) {
            return new MyChildClass[0];
        }
    };

    protected MyChildClass(Parcel in) {
        super(in);
    }
}

In the FirstActivity I was passing the parcelable, I wrote, but I get an error related to the constructor of the MyChildClass:

    MyChildClass objChildClass = new MyChildClass();   //I got an error here, expecting a Parcel object?!
    Intent intent = new Intent(this,SecondActivity.class);
    intent.putExtra("extraParcelable",objChildClass);
    startActivity(intent);

According to the Android Studio, MyChildClass expects a Parcel argument to MyChildClass, but how can I pass a Parcel if I want to send this class through an Intent?

EDIT

I added an empty constructor in both MyChildClass and MyAbstractClass:

MyChildClass(){super()}

And In the MyAbstractClass:

MyAbstractClass(){}

In the FirstActivity I added:

objChildClass.setDField = 2.5;
objChildClass.setObjEnumType = MyEnum.Type1;
objChildClass.setStringResource = getString(R.string.app_name);
Intent intent = new Intent(this,SecondActivity.class);
intent.putExtra("extraParcelable",objChildClass);
startActivity(intent);

In The SecondActivity:

class SecondActivity : AppCompatActivity() {
     private val lbResult by lazy {findViewById<TextView>(R.id.lb_result)}
     private val objReceived by lazy {intent?.extras?.getParcelable<MyAbstractClass>("extraParcelable")}

     override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //
        //...
        //
        lbResult.text = objReceived?.dField.toString()  //I get a null here
      }
}

By I get a null in lbResults. The code was working perfectly when I used intent.putExtras(String, Double), but now using parcelable is null if I use an empty constructor. Otherwise I get that error wanting to pass a Parcel as a constructor, (As I wrote above)


Solution

  • The problems I was facing with implementing Parcelable were due to simple mistakes I ignored about the constructors when dealing with Parcelables:

    • First, if the class which implements Parcelable didn't have a constructor, it has to create a constructor for the class Parcelable along with the constructor receiving the Parcel. If not, Android Studio or the compiler will complain about the Parcel not being supplied as argument (because the only constructor in the class requires a Parcel object).
    public MyChildClass(Parcel parcel){
        super(parcel);
    }
    public MyChildClass(){super();}
    
    

    I had also to match the constructor on the super class:

    protected MyAbstractClass(Parcel in){
       dField = in.readDouble();
       this.objEnumType = MyEnum.valueOf(in.readString());
       stringResource = in.readInt();
    }
    protected MyAbstractClass(){}
    
    • Second, the constructor receiving the Parcel object must be called in createFromParcel method inside Parcelabe.Creator CREATOR. This was my second mistake. Android studio autogenerated Parcelable.Creator only puts return null in this method.
        public static final Parcelable.Creator<MyChildClass> CREATOR = new Creator<MyChildClass>() {
            @Override
            public MyChildClass createFromParcel(Parcel parcel) {
                return new MyChildClass(parcel);   //It needs to replace the default `return null` by the instance of the class calling the constructor to pass the Parcel object
            }
            @Override
            public MyChildClass[] newArray(int i) {
                return new MyChildClass[0];
            }
        };
    
    • Third: Since the method public T createFromParcel from Parcelable.Creator CREATOR requires an instance of the class, I removed Parcelable.Creator CREATOR from MyAbstractClass, since being an abstract class I cannot create any instances there

    Now I can pass my derived classes through the activities without null or complaining about the Parcel not passed throught the constructor