Android Parcelable Cannot instantiate the type - java

I am working on an Android App, and I am trying to pass information using Parcelable. So here's what I've got.
import android.os.Parcel;
import android.os.Parcelable;
abstract class Role implements Parcelable {
private String name;
private String image;
public Role() {
}
public Role(Parcel read) {
name = read.readString();
image = read.readString();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String toString() {
return this.name;
}
public static final Parcelable.Creator<Role> CREATOR =
new Parcelable.Creator<Role>() {
public Role createFromParcel(Parcel source) {
return new Role(source);
}
public Role[] newArray(int size) {
return new Role[size];
}
};
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeString(name);
dest.writeString(image);
}
}
However, when I try to compile I get the Error (where I placed the comment)
Cannot instantiate the Type Role
Any thoughts on this?
Best regards

I have not used parcelable in abstract class myself, but it should be ok. You may want to check here or more generally here
I have a VERY similar class (two strings) but its a public static class.
I do new() on my string members in the constructor.

Yout class Role is defined as abstract, the abstract classes cannot be instantiated.
just define your class Role:
class Role implements Parcelable {
//...
}

As qjuanp mentioned, one cannot instantiate an abstract class (as per Java's and common OOP definition; you cannot instantiate something that is abstract, it has got to be more defined).
I'm sure you're trying to use some subclasses of Role (that's about the only way you can use both abstract and implement Parcelable here), consider using this approach:
public abstract class A implements Parcelable {
private int a;
protected A(int a) {
this.a = a;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(a);
}
protected A(Parcel in) {
a = in.readInt();
}
}
public class B extends A {
private int b;
public B(int a, int b) {
super(a);
this.b = b;
}
public static final Parcelable.Creator<B> CREATOR = new Parcelable.Creator<B>() {
public B createFromParcel(Parcel in) {
return new B(in);
}
public B[] newArray(int size) {
return new B[size];
}
};
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(b);
}
private B(Parcel in) {
super(in);
b = in.readInt();
}
}

Related

Parcelable.CREATOR on abstract class

I'm trying to pass an ArrayList of unknown class type that extend an abstract class, to another activity using Parcelable. Since its not possible to use Parcelable.CREATOR on the abstract class, there is an error when I try to create the ArrayList: in.readTypedList(AbstractChannel.CREATOR), see below:
public class TvNetwork implements Parcelable {
public String name;
public ArrayList<? extends AbstractChannel> mChannels;
public TvNetwork(String name, ArrayList<? extends AbstractChannel> channels) {
this.name = name;
this.mChannels = channels;
}
protected TvNetwork(Parcel in) {
name = in.readString();
mChannels = in.readTypedList(AbstractChannel.CREATOR); // here is the error
}
public static final Creator<TvNetwork> CREATOR = new Creator<TvNetwork>() {
#Override
public TvNetwork createFromParcel(Parcel in) {
return new TvNetwork(in);
}
#Override
public TvNetwork[] newArray(int size) {
return new TvNetwork[size];
}
};
public ArrayList<? extends AbstractChannel> getChannels() {
return mChannels;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeTypedList(mChannels);
}
}
Writing seems to work but not reading. This obviously does not work either, but explains a bit more what I want to do:
in.readTypedList(mChannels, <? extends AbstractChannel>.class.getClassLoader());
Any ideas?

Android AIDL with Java generics

Does Android AIDL support generics?
For example, assume that I have a class Result<T>, where T can be any type including primitives (via autoboxing) or other custom classes such as Car. Any custom classes implement Parcelable as required by Binder.
Then possible AIDL method signatures would be
Result<Car> m1();
Result<Void> m2();
Result<Boolean> m3();
From what I could gather, the AIDL compiler doesn't like things like Result<Animal> getResult();. However, Result getResult(); does work. So this is what I did:
Created a class with the signature public class Result<T extends Parcelable> implements Parcelable.
Created a new class to throw into the first one, which is called Animal. The signature is public class Animal implements Parcelable.
Had to implement methods required by interface Parcelable and a CREATOR in both Result and Animal, and also created one AIDL for each as is required and imported both classes in the main AIDL. This stuff is regular AIDL work and is describe in the AIDL site.
Inside Result, we store not only an object of type T but also a Class object. When writing the parcel we need to write first the class type and only then the generic object. When reading, we do it in the same order. We need to write the class type because when we read we have to do t = (T) in.readValue(classType.getClassLoader()); and without a class type we do not know which class loader to fetch. There are probably other ways to do this but this is how I've done it for this example.
When receiving on the client node, I can successfully do Result<Animal> r = MainActivity.this.service.getResult(); and then call methods on both Result and Animal.
Some code that will hopefully makes things more clearer can be found below.
public class Result<T extends Parcelable> implements Parcelable {
private String msg;
private Class classType;
private T value;
public Result(String msg, T value, Class classType) {
this.msg = msg;
this.value = value;
this.classType = classType;
}
// to reconstruct object
public Result(Parcel in) {
readFromParcel(in);
}
public String getMsg() {
return msg;
}
public T getValue() {
return value;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(msg);
dest.writeValue(classType);
dest.writeValue(value);
}
private void readFromParcel(Parcel in) {
this.msg = in.readString();
this.classType = (Class) in.readValue(Class.class.getClassLoader());
this.value = (T) in.readValue(classType.getClassLoader());
}
public static final Creator<Result> CREATOR = new Creator<Result>() {
#Override
public Result createFromParcel(Parcel source) {
return new Result(source);
}
#Override
public Result[] newArray(int size) {
return new Result[size];
}
};
}
public class Animal implements Parcelable {
private int n;
public Animal(int n) {
this.n = n;
}
public Animal(Parcel in) {
readFromParcel(in);
}
public int getN() {
return n;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(n);
}
private void readFromParcel(Parcel in) {
n = in.readInt();
}
public static final Creator<Animal> CREATOR = new Creator<Animal>() {
#Override
public Animal createFromParcel(Parcel source) {
return new Animal(source);
}
#Override
public Animal[] newArray(int size) {
return new Animal[size];
}
};
}
Excerpt from the Service:
#Override
public Result getResult() throws RemoteException {
Result<Animal> r = new Result<Animal>("this is an animal", new Animal(42), Animal.class);
return r;
}
Excerpt from the Client:
Result<Animal> r = MainActivity.this.service.getResult();
Log.d(TAG, "Received the following (Outer): " + r.getMsg());
Log.d(TAG, "Received the following (Inner): " + r.getValue().getN());
Another way to do it is changing the signature of Result into public class Result<T extends Serializable> implements Parcelable, making Animal implement Serializable, and then use dest.writeSerializable(value); and this.value = (T) in.readSerializable(); inside Result.
With this approach there is no need to send the class type to the other side or even use it at all. You will, nonetheless, pay the price.
Daniels solution almost worked for me except the thing with marshalling and unmarshaling classtype.
Instead of "dest.writeValue(classType);" and "this.classType = (Class) in.readValue(Class.class.getClassLoader());" I had to use "dest.writeSerializable(classType);" and "classType = (Class) in.readSerializable();" and it worked like a charm
Thank you Daniel

Write enum with String to parcel

I had Parcelable enum like this:
public enum Option implements Parcelable {
DATA_BASE, TRIPS, BIG_PHOTOS,
OLD_PHOTOS, FILTERS_IMAGES,
CATEGORIES_IMAGES, PAGES,
SOUNDS, PUBLIC_TRANSPORT, MAPS;
public static final Parcelable.Creator<Option> CREATOR = new Parcelable.Creator<Option>() {
public Option createFromParcel(Parcel in) {
return Option.values()[in.readInt()];
}
public Option[] newArray(int size) {
return new Option[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(ordinal());
}
}
Now I modified it and it looks like:
public enum Option implements Parcelable {
DATA_BASE("Database"), TRIPS("Trips"), BIG_PHOTOS("BigPhotos"),
OLD_PHOTOS("OldPhotos"), FILTERS_IMAGES("FiltersImages"),
CATEGORIES_IMAGES("CategoriesImages"), PAGES("Pages"),
SOUNDS("Sounds"), PUBLIC_TRANSPORT("PublicTransport"), MAPS("Maps");
private String option;
Option(String option){
this.option = option;
}
public String getName(){
return option;
}
public static final Parcelable.Creator<Option> CREATOR = new Parcelable.Creator<Option>() {
public Option createFromParcel(Parcel in) {
//...
}
public Option[] newArray(int size) {
//...
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
//...
}
}
How should I implement writeToParcel(), createFromParcel() and newArray() int this Enum? I need that to pass it through extra in intent.
This is an old question but there is a better solution:
dest.writeString(myEnumOption.name());
and
myEnumOption = MyEnum.valueOf(in.readString());
I solved it by:
public enum Option implements Parcelable {
DATA_BASE("Database"), TRIPS("Trips"), BIG_PHOTOS("BigPhotos"),
OLD_PHOTOS("OldPhotos"), FILTERS_IMAGES("FiltersImages"),
CATEGORIES_IMAGES("CategoriesImages"), PAGES("Pages"),
SOUNDS("Sounds"), PUBLIC_TRANSPORT("PublicTransport"), MAPS("Maps");
private String option;
Option(String option){
this.option = option;
}
public String getName(){
return option;
}
private void setOption(String option){
this.option = option;
}
public static final Parcelable.Creator<Option> CREATOR = new Parcelable.Creator<Option>() {
public Option createFromParcel(Parcel in) {
Option option = Option.values()[in.readInt()];
option.setOption(in.readString());
return option;
}
public Option[] newArray(int size) {
return new Option[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(ordinal());
out.writeString(option);
}
}
It may be late for the OP, but it may help others, so I'll still post this.
As it was mentioned before, it's a bad practice to have a setter to an enum's member. If you need that member, make it final (which excludes the possibility of a setter for it)
You don't need to save that member (in your case the String 'option') into the Parcel and restore it on recreation.
My implementation would be as follows
public enum Option implements Parcelable {
DATA_BASE("Database"), TRIPS("Trips"), BIG_PHOTOS("BigPhotos"),
OLD_PHOTOS("OldPhotos"), FILTERS_IMAGES("FiltersImages"),
CATEGORIES_IMAGES("CategoriesImages"), PAGES("Pages"),
SOUNDS("Sounds"), PUBLIC_TRANSPORT("PublicTransport"), MAPS("Maps");
private final String option;
Option(String option){
this.option = option;
}
public String getName() {
return option;
}
public static final Parcelable.Creator<Option> CREATOR = new Parcelable.Creator<Option>() {
public Option createFromParcel(Parcel in) {
return values()[in.readInt()];
}
public Option[] newArray(int size) {
return new Option[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(ordinal());
}
}
If the only reason you're making your enum Parcelable is to pass it in an Intent, you don't need to to that. enums are Serializable, so you can pass it like:
intent.putExtra("EnumValue", Option.DATA_BASE);
and retrieve it using:
Option myEnum = (Option) intent.getSerializableExtra("EnumValue");

Creating parcelable class

my class looks like this when I run my application and navigate throug the different fragments sometimes it crashes and logcat says that error is BadParcelableException: Parcelable protocol requires a Parcelable.Creator object called CREATOR on class ge.mobility.weather.entity.City
here is my code
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.List;
public class City implements Parcelable {
private String code;
private String name;
private List<CityWeather> weathers ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public List<CityWeather> getWeathers() {
if(weathers == null) {
weathers = new ArrayList<CityWeather>();
}
return weathers;
}
public void addCityWeather(CityWeather w) {
getWeathers().add(w);
}
public void addCityWeathers(List<CityWeather> w) {
getWeathers().addAll(w);
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {enter code here
// TODO Auto-generated method stub`enter code here`
}
}
You need to implement the Parcelable.Creator and also add the un/serialise methods and a constructor:
public City(Parcel in) {
readFromParcel(in);
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(code);
dest.writeString(name);
dest.writeTypedList(weathers);
}
private void readFromParcel(Parcel in) {
code = in.readString();
name = in.readString();
in.readTypedList(weathers, CityWeather.CREATOR);
}
public static final Parcelable.Creator<City> CREATOR = new Parcelable.Creator<City>() {
public City createFromParcel(Parcel in) {
return new City(in);
}
public City[] newArray(int size) {
return new City[size];
}
};
You will also need to implement the Parcelable methods for the CityWeather class.
Try this code generation library Parceler , if you are use InteliJ Use this plugin IntelliJ Plugin for Android Parcelable boilerplate code generation

Help with creating a Parcelable class

I am trying to create a Parcelable class. The class extends TableRow:
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.widget.TableRow;
public class AcmTableRow extends TableRow implements Parcelable{
private int index;
public boolean isSection;
private String volumeLink;
private String rowId;
private String cfr;
private static Context context;
public AcmTableRow(Context context) {
super(context);
}
public AcmTableRow(Context context, Parcel in) {
super(context);
AcmTableRow.context = context;
readFromParcel(in);
}
private void readFromParcel(Parcel in) {
//strValue = in.readString();
//intValue = in.readInt();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
//dest.writeString(strValue);
//dest.writeInt(intValue);
}
public void setIndex(int index) {
this.index = index;
}
public int getIndex() {
return index;
}
public void setRowId(String rowId) {
this.rowId = rowId;
}
public String getRowId() {
return rowId;
}
public void setCfr(String cfr) {
this.cfr = cfr;
}
public String getCfr() {
return cfr;
}
public void setVolumeLink(String volumeLink) {
this.volumeLink = volumeLink;
}
public String getVolumeLink() {
return volumeLink;
}
public static final Parcelable.Creator<AcmTableRow> CREATOR = new Parcelable.Creator<AcmTableRow>() {
public AcmTableRow createFromParcel(Parcel in) {
return new AcmTableRow(context, in);
}
public AcmTableRow[] newArray(int size) {
return new AcmTableRow[size];
}
};
}
I am confused as what I need to put in:
readFromParcel(Parcel in)
And
writeToParcel(Parcel dest, int flags)
Any explanation or help is greatly appreciated.
Thank you
Phil
The writeToParcel method simply flattens the object into a parcel. You'll use this when you need to pass your object between activities. In your case, it should look like this:
public void writeToParcel (Parcel dest, int flags)
{
dest.writeInt(index);
dest.writeBoolean(isSection);
dest.writeString(volumeLink);
dest.writeString(rowId);
dest.writeString(cfr);
}
I've never had to use readFromParcel and I'm not sure you need to here either. Your object will be created from the Parcel when you call the appropriate method in your next activity.
Just a quick note, you probably don't want to pass the context in your parcelable class. I'm not even sure you can. You'll need to assign the context when you inflate the parcelable again later on.
This is a simple working Parcelable example:
public class ParcelableExample implements Parcelable {
private int intMember;
private String stringMember;
/*
* Getters and setters
* ....
*/
/*
* Constructors
* ....
*/
/*
* Custom parcelable implementation
*/
private ParcelableExample(Parcel in) {
// notice that we are reading in the same order as we have written
intMember = in.readInt();
stringMember = in.readString();
}
#Override
public int describeContents() {
return hashCode();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(intMember);
dest.writeString(stringMember);
}
#SuppressWarnings("rawtypes")
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public ParcelableExample createFromParcel(Parcel in) {
return new ParcelableExample(in);
}
public ParcelableExample[] newArray(int size) {
return new ParcelableExample[size];
}
}

Categories

Resources