I'm using the class Tests as the base class, the I created three more classes, Test1, Test2, Test3, they extends Tests class, then I have one more class, States which has an Arraylist.
States is used to gather a bunch of info including a list with the tests I want to perform, so I use the Arraylist and the method "add" to add test1, test2, or test3 to the list, then I want to send this State object to the activity B. I've implemented the parcelable interface on classes Test1, Test2, Test3 and States but I'm getting the next exception:
Unmarshalling unknown type code 6357090 at offset 300
Please, can suggest any way to achieve this, It's important to gather the tests on the arraylist, i think there lies the problem, thanks.
Sorry, this is too long for a comment, so I posted as an answer
Since Test1 extends Tests, Tests should haveit's own Parcelable implementation.
This implementation is the called by all its 'child' classes by using super. For example (this is what I use in my apps):
Tests class
public class Tests implements Parcelable {
private int Id;
private String Name;
// parcelable
protected Tests(Parcel in) {
Id = in.readInt();
Name = in.readString();
}
public static final Creator<Tests> CREATOR = new Creator<Tests>() {
#Override
public Tests createFromParcel(Parcel in) {
return new Tests(in);
}
#Override
public Tests[] newArray(int size) {
return new Tests[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(Id);
dest.writeString(Name);
}
}
Test1 class
public class Test1 extends Tests implements Parcelable {
private int Score;
// parcelable
protected Test1(Parcel in) {
super(in);
Score = in.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(Score);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<Test1> CREATOR = new Creator<Test1>() {
#Override
public Test1 createFromParcel(Parcel in) {
return new Test1(in);
}
#Override
public Test1[] newArray(int size) {
return new Test1[size];
}
};
}
Related
Hey guys ive found some tutorials about sending non primitive object to activity via intent. But see only that they have members of only primitive in all examples.
I have a class with members that are user data types.
How do i send an object with implementing Parcelable with non primitive instance variables like arraylist etc?
Thanks
The objects that are members of your class must also be Parcelable (or Serializable), and any objects they include must also be Parcelable (or Serializable). To summarize, a Parcelable object must have fields that are either: primitives, Parcelable objects (and their supported collections such as Map or ArrayList) or Serializable objects(and their supported collections such as Map or ArrayList).
A sample piece of code demonstrating this (the Foo class is a Parcelable which contains Bar, which is also Parcelable), is the following (in Java):
import android.os.Parcel;
import android.os.Parcelable;
public class Foo implements Parcelable {
private int primitive;
private Bar object;
public Foo() {
primitive = 0;
object = null;
}
private Foo(final Parcel in) {
primitive = in.readInt();
object = in.readParcelable(Bar.class.getClassLoader());
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(primitive);
dest.writeParcelable(object, flags);
}
public static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>() {
public Foo createFromParcel(Parcel in) {
return new Foo(in);
}
public Foo[] newArray(int size) {
return new Foo[size];
}
};
}
and the Bar class:
import android.os.Parcel;
import android.os.Parcelable;
public class Bar implements Parcelable {
private String attribute;
public Bar() {
attribute = "";
}
private Bar(final Parcel in) {
attribute = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(attribute);
}
public static final Parcelable.Creator<Bar> CREATOR = new Parcelable.Creator<Bar>() {
public Bar createFromParcel(Parcel in) {
return new Bar(in);
}
public Bar[] newArray(int size) {
return new Bar[size];
}
};
}
e.g
Class City that needs to implement Parcelable has field of type Location which implements Serializable. class Location is imported from a third party jar file and I cannot modify it. How do I successfully implement Parcelable for class City with the Location field ?
Simply use Parcel.writeSerializable() and Parcel.readSerializable()
public class MyParcelableObject implements Parcelable {
public static final Parcelable.Creator<MyParcelableObject> CREATOR =
new Parcelable.Creator<MyParcelableObject>() {
#Override
public MyParcelableObject createFromParcel(Parcel in) {
return new MyParcelableObject(in);
}
#Override
public MyParcelableObject[] newArray(int size) {
return new MyParcelableObject[size];
}
};
private final MySerializableObject mySerializableField;
private MyParcelableObject(Parcel in) {
this.mySerializableField = (MySerializableObject) in.readSerializable();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeSerializable(mySerializableField);
}
#Override
public int describeContents() {
return 0;
}
}
To pass an arrayList of objects to a fragment, I have to make the list objects parcelable.
public mObjectClass implements Parcelable {
// Some code
}
The problem is that one of the attributes in my list objects is another object-based arrayList.
public mObjectClass implements Parcelable {
// Some code
private ArrayList<someOtherObject> anotherArrayList;
}
How can I make mObjectClass parcelable?
someOtherObject has to implement Parcelable (not extend has in your question) too. Then you can call parcel.writeTypedList(anotherArrayList); to write it and parcel.readTypedList(yourList, someOtherObject.CREATOR) to read it back. You can read more here
This solution is heavily influenced by some Stackoverflow posts, including
this.
Essentially make both Classes parcelable and make use of someOtherClasses Parcelable.Creator.
mObjectClass:
public class mObjectClass implements Parcelable {
private ArrayList<someOtherObject> anotherArrayList;
//add getter + setter...
public mObjectClass() {
anotherArrayList = new ArrayList<someOtherObject>();
}
public mObjectClass(Parcel in) {
anotherArrayList = new ArrayList<someOtherObject>();
in.readTypedList(anotherArrayList, someOtherObject.CREATOR);
}
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel outParcel, int flags) {
outParcel.writeTypedList(anotherArrayList);
}
public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
#Override
public mObjectClass createFromParcel(Parcel in) {
return new mObjectClass(in);
}
#Override
public mObjectClass[] newArray(int size) {
return new mObjectClass[size];
}
};
}
someOtherObject:
public class someOtherObject implements Parcelable {
String someString;
//add getter + setter...
public void writeToParcel(Parcel out, int flags) {
out.writeString(someString);
}
public static final Parcelable.Creator<someOtherObject> CREATOR = new Parcelable.Creator<someOtherObject>() {
public someOtherObject createFromParcel(Parcel in) {
return new someOtherObject(in);
}
public someOtherObject[] newArray(int size) {
return new someOtherObject[size];
}
};
public int describeContents() {
return 0;
}
public someOtherObject(Parcel in) {
someString = in.readString();
}
public someOtherObject() {
}
public someOtherObject(String someString) {
this.someString = someString;
}
}
Tada, you are now able to add mObjectClass as extra to your intents after initialising it and using a setter to set the Arraylist with other someOtherObjects.
My Parcelable Class :
public class Category implements Parcelable{
int id;
String name;
Department department;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
dest.writeParcelable(department, 10);
}
public static final Parcelable.Creator<Category> CREATOR = new Parcelable.Creator<Category>() {
public Category createFromParcel(Parcel in) {
return new Category(in);
}
public Category[] newArray(int size) {
return new Category[size];
}
};
public Category(Parcel in){
id=in.readInt();
name=in.readString();
department= in.readParcelable(department.getClass().getClassLoader());
}
When i send object of category class to other activity, then writeToparcel() and createFromParcel() method got called, I observed NullPointerException at
department= in.readParcelable(department.getClass().getClassLoader());
But while debugging i had checked that in writeToParel() method department object is stored correct, but how that is not returned back, in createFromParcel() , Same code is running fine in Android Studio 1.1 .
Currently I had changed my implementation code like that :
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
dest.writeParcelable(department, Constants.PARCELABLE_DEPARTMENT);
dest.writeInt(department.getId());
dest.writeString(department.getName());
}
public Category(Parcel in){
id=in.readInt();
name=in.readString();
department=new Department(in.readInt(),in.readString());
}
Now, the code is working fine , but now i need to create extra department object, which i think is not good way .
Does any one have any solution regarding the problem ?
department.getClass().getClassLoader()
This is what throws the error. department == null and you're trying to fetch it's class. Therefore it throws an NPE.
Instead, fetch the class loader via the class object:
Department.class.getClassLoader()
I'm implementing Parcelable class that has another Parcelable insde.
In OuterParcelable class:
#Override
public void writeToParcel(Parcel dest, int flags) {
Bundle tmp = new Bundle();
tmp.putParcelable("innerParcelable", mParcelable);
dest.writeBundle(tmp);
and then:
public OuterParcelable(Parcel parcel) {
super();
Bundle b = parcel.readBundle();
mParcelable = b.getParcelable("innerParcelable");
and:
public OuterParcelable createFromParcel(Parcel in) {
return new OuterParcelable(in);
}
When I recreate object using above code I get:
08-18 17:13:08.566: ERROR/AndroidRuntime(15520): Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: my.package.InnerParcelable
A clean way to store non-primitive attributes as parcelable, possibly null, values. Use Parcel.writeValue() and readValue(). See comments in code below:
public class MyParcelableClass implements Parcelable {
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeValue(getIntegerAttribute()); // getIntegerAttribute() returns Integer
dest.writeValue(getDoubleAttribute());
dest.writeValue(getMyEnumAttribute()); // getMyEnumAttribute() returns a user defined enum
dest.wrtieValue(getUserClassAttribute()); //UserClass must implement Parcelable in a similar fashion
}
private MyParcelableClass(Parcel in) {
setIntegerAttribute((Integer)in.readValue(null)); //pass null to use default class loader. Ok for Integer, String, etc.
setDoubleAttribute((Double)in.readValue(null)); //Cast to your specific attribute type
setEnumAttribute((MyEnum)in.readValue(null));
setUserClassAttribute((UserClass)in.readValue(UserClass.class.getClassLoader())); //Use specific class loader
}
#Override
public int describeContents() ...
public static final Parcelable.Creator<ParcelableLocationBean> CREATOR ...
}
Works like a charm. writeValue() and readValue() encapsulate the dealing with possible nulls and type detection. From javadoc:
public final void writeValue (Object v) Flatten a generic object
in to a parcel. The given Object value may currently be one of the
following types: null, String, Integer, ... String[],
boolean[], ... Any object that implements the Parcelable protocol. ...
Why are you putting the value into a Bundle? Did you completely implement the parcelable in your class?
Parcelable Skeleton
public MyClass(Parcel in) {
readFromParcel(in);
}
//
// Parcelable Implementation
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(aParcelableClass, flags);
}
private void writeObject(Parcel dest, Object obj) {
if (obj != null) {
dest.writeInt(1);
dest.writeValue(obj);
} else {
dest.writeInt(0);
}
}
public void readFromParcel(Parcel in) {
aParcelableClass = in.readParcelable(ParcelableClass.class.getClassLoader());
}
private Object readObject(Parcel in) {
Object value = null;
if (in.readInt() == 1) {
value = in.readValue(null); // default classloader
}
return value;
}
public static final Parcelable.Creator<MyClass> CREATOR = new Parcelable.Creator<MyClass>() {
#Override
public MyClass createFromParcel(Parcel source) {
return new MyClass(source);
}
#Override
public MyClass[] newArray(int size) {
return new MyClass[size];
}
};
I added a few things to make null values more easily dealt with, but the principle is the same. You need the #Override items, constructor, and Creator.
If you're going to read and write a parcelable you will have issues if you specify null as the class loader.