I created my own Parcelable class based on a snippet to send custom data via an Intent. Using it, Android (Min. API 10) gives me an exception, what it wrong with that piece of code below? I broke it down to a minimum. Here it is:
public class MyParcelable implements Parcelable {
private float[] data = null;
public MyParcelable(float[] data) {
this.data = data;
}
public MyParcelable(Parcel in) {
/* After this line the exception is thrown */
in.readFloatArray(data);
}
public static final Creator<MyParcelable> CREATOR = new Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
public int describeContents() {
return this.hashCode();
}
public void writeToParcel(Parcel out, int flags) {
out.writeFloatArray(data);
}
public float[] getData() {
return data;
}
}
After searching for a solution quite a while I stumbled upon this post where LionKing gave a working hint.
The Parcelable class now looks like this:
public class MyParcelable implements Parcelable {
private float[] data = null;
public MyParcelable(float[] data) {
this.data = data;
}
public MyParcelable(Parcel in) {
/* The exception is gone */
data = in.createFloatArray();
}
public static final Creator<MyParcelable> CREATOR = new Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
public int describeContents() {
return this.hashCode();
}
public void writeToParcel(Parcel out, int flags) {
out.writeFloatArray(data);
}
public float[] getData() {
return data;
}
}
This solution works also with the other base type arrays.
Related
I've a custom parcelable (simplified) that contains a string array:
public class MyClass implements Parcelable {
public static final Creator<MyClass> CREATOR = new Creator<MyClass>() {
public MyClass createFromParcel(Parcel in) {
return new MyClass(in);
}
public MyClass[] newArray(int size) {
return new MyClass[size];
}
};
#SerializedName(“tips”)
private List<String> Tips;
public MyClass() {
Tips = new ArrayList<>();
}
protected Category(Parcel in) {
Tips = in.createStringArrayList();
}
public List<String> getTips() {
return Tips;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringList(Tips);
}
}
I'm trying to pass this parcelable via Intent to another activity. The process is VERY slow and sometimes I get an OOM exception...I don't understand why, I just want to pass a string array...
Save your array into database and pass id of that record.
I have a class A with and inner class B. like the code bellow. I dont know where I am going wrong. What would be the proper way to make Class A Parcelable? Here is how i have right now and its no good. Thanks!
public class A implements Parcelable
{
private B[][] bArray;
public A(Parcel in)
{
bArray = (B[][]) in.readSerializable();
}
#Override
public void writeToParcel(Parcel parcel, int i)
{
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public A createFromParcel(Parcel in) {
return new A (in);
}
public A[] newArray(int size) {
return new A[size];
}
};
static class B implements Parcelable
{
public B(Parcel in)
{
}
#Override
public void writeToParcel(Parcel parcel, int i)
{
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public B createFromParcel(Parcel in) {
return new B(in);
}
public B[] newArray(int size) {
return new B[size];
}
};
}
}
First of all i have check this answer.
What i am trying to do is extending Location class calling it LocationPlus which has some
member variables. functionality i am trying to achieve is pass the object of LocationPlus class from one activity to another.
Here is my CREATOR
public static final Parcelable.Creator<LocationPlus> CREATOR = new Parcelable.Creator<LocationPlus>() {
#Override
public LocationPlus createFromParcel(Parcel source) {
return new LocationPlus(source);
}
#Override
public LocationPlus[] newArray(int size) {
return new LocationPlus[size];
}
};
problem i am facing is this error
Implicit super constructor Location() is undefined. Must explicitly invoke another constructor
when trying to write constructor
public LocationPlus(Parcel in) {
Someone in comment ask me to post LocationPlus class so here it is
public class LocationPlus extends Location{
private int mBattery = -1;
public LocationPlus(String locationName) {
super(locationName);
}
public LocationPlus(Location location) {
super(location);
}
public int getmBattery() {
return mBattery;
}
public void setmBattery(int mBattery) {
this.mBattery = mBattery;
}
#Override
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<LocationPlus> CREATOR = new Parcelable.Creator<LocationPlus>() {
#Override
public LocationPlus createFromParcel(Parcel source) {
return new LocationPlus(source);
}
#Override
public LocationPlus[] newArray(int size) {
return new LocationPlus[size];
}
};
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(mBattery);
}
public LocationPlus(Parcel in) {
mBattery =in.readInt();
}
}
Parcelable, the Speed King
According to google engineers, this code will run significantly faster. One of the reasons for this is that we are being explicit about the serialization process instead of using reflection to infer it. It also stands to reason that the code has been heavily optimized for this purpose.
public abstract class BaseClass implements Parcelable {
public String FullName;
public boolean IsValidUser;
public String UserName;
public BaseClass () {
}
protected BaseClass(Parcel in) {
FullName = in.readString();
IsValidUser = in.readByte() != 0;
UserName = in.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(FullName);
dest.writeByte((byte) (IsValidUser ? 1 : 0));
dest.writeString(UserName);
}
}
Child class will be as follows with usage of list adding into parcelable object:
public class DerivedClass extends BaseClass {
public boolean IsSuccess;
public String Message;
public List<AnotherClass> AnotherClassObj;
public DerivedClass () {
super();
}
protected DerivedClass(Parcel in) {
super(in);
AnotherClassObj = new ArrayList<AnotherClass>();
IsSuccess = in.readByte() != 0;
Message = in.readString();
AnotherClassObj = in.readArrayList(AnotherClass.class.getClassLoader());
}
public static final Creator<DerivedClass> CREATOR = new Creator<DerivedClass>() {
#Override
public DerivedClass createFromParcel(Parcel in) {
return new DerivedClass(in);
}
#Override
public DerivedClass[] newArray(int size) {
return new DerivedClass[size];
}
};
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeByte((byte) (IsSuccess ? 1 : 0));
dest.writeString(Message);
dest.writeList(AnotherClassObj);
}
public int describeContents() {
return 0;
}
}
Another child class :
public class AnotherClass extends BaseClass {
public AnotherClass() {
super();
}
protected AnotherClass(Parcel in) {
super(in);
}
public int describeContents() {
return 0;
}
public static final Creator<AnotherClass> CREATOR = new Creator<AnotherClass>() {
#Override
public AnotherClass createFromParcel(Parcel in) {
return new AnotherClass(in);
}
#Override
public AnotherClass[] newArray(int size) {
return new AnotherClass[size];
}
};
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
}
}
In Activity:
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
intent.putExtra("UserObject", parcelableObject);
startActivity(intent);
finish();
In receiving activity:
Bundle extras = getIntent().getExtras();
if (extras != null) {
userObject = extras.getParcelable("UserObject");
}
Hi I've do research a lot about this, but I couldn't find useful anything. I try solution below and it worked for me.
Let say your super class has only int variable named "mData".
public class Location implements Parcelable {
protected int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<Location> CREATOR
= new Parcelable.Creator<Location>() {
public Location createFromParcel(Parcel in) {
return new Location(in);
}
public Location[] newArray(int size) {
return new Location[size];
}
};
private Location(Parcel in) {
mData = in.readInt();
}
}
Then, your extended class has only int variable named "mBattery".
public class LocationPlus extends Location {
protected int mBattery;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mBattery);
}
public static final Parcelable.Creator<LocationPlus> CREATOR
= new Parcelable.Creator<LocationPlus>() {
public LocationPlus createFromParcel(Parcel in) {
return new LocationPlus(in);
}
public LocationPlus[] newArray(int size) {
return new LocationPlus[size];
}
};
private LocationPlus(Parcel in) {
mBattery = in.readInt();
}
}
So far, LocationPlus works fine. But we don't set variable of super class. Firstly, I set super class' variables on extended class with super(..) method. But it didn't work.
private LocationPlus(Parcel in) {
super(in);
mBattery = in.readInt();
}
Instead of code above, you should set all super class' variables explicitly. Super class' variables should be protected. Final constructor should be like this:
private LocationPlus(Parcel in) {
mData = in.readIn();
mBattery = in.readInt();
}
and writeToParcel method should be like this:
public void writeToParcel(Parcel out, int flags) {
out.writeIn(mData);
out.writeInt(mBattery);
}
Try this solution:
public static final Parcelable.Creator<LocationPlus> CREATOR =
new Parcelable.Creator<LocationPlus>() {
#Override
public LocationPlus createFromParcel(Parcel in) {
Location l = Location.CREATOR.createFromParcel(in);
LocationPlus lp = new LocationPlus(l);
lp.mBattery= in.readInt();
return lp;
}
#Override
public LocationPlus[] newArray(int size) {
return new LocationPlus[size];
}
};
#Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeInt(mBattery);
}
According to the Android docs, there isn't a Location() constructor for the Location class. When initializing your LocationPlus class, you need to call either super(String provider) or super(Location l).
Edit: Corrected syntax
(See Location Android Doc)
The question is: how to implement parceling efficiently on parcelables which implement an interface.
I have this case. An interface that is implemented by two concrete classes (see Picture below) and a container that should be Parcelable (PictureProviders).
The questions is: how to parcel efficiently the list of pictures?
We cannot use read/writeTypedList because we won't know which creator to pass to readTypedList.
We can use read/writeParcelableArray, but that means to copy the List to an array. Also which class loader would you use? would getClass().getClassLoader() be ok?
We could also use read/writeList. Which class loader would you use? would getClass().getClassLoader() be ok?
Sample code:
public interface PictureProvider extends Parcelable {
public Collection<Picture> getAvailablePictures();
}
public class SimplePictureProvider implements PictureProvider {
// ...
private List<Picture> pictures;
public SimplePictureProvider(Parcel in) {
// THIS IS WHERE THE QUESTION APPLIES
}
#Override
public void writeToParcel(Parcel dest, int flags) {
// THIS IS WHERE THE QUESTION APPLIES
}
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<SimplePictureProvider> CREATOR = new Parcelable.Creator<SimplePictureProvider>() {
public SimplePictureProvider createFromParcel(Parcel in) {
return new SimplePictureProvider(in);
}
public SimplePictureProvider[] newArray(int size) {
return new SimplePictureProvider[size];
}
};
}
public interface Picture extends Parcelable {
public byte[] getPictureData();
}
public class RemotePicture implements Picture {
// ...
private String pictureUrl;
public RemotePicture(Parcel in) {
pictureUrl = in.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(pictureUrl);
}
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<RemotePicture> CREATOR = new Parcelable.Creator<RemotePicture>() {
public RemotePicture createFromParcel(Parcel in) {
return new RemotePicture(in);
}
public RemotePicture[] newArray(int size) {
return new RemotePicture[size];
}
};
}
public class LocalPicture implements Picture {
// ...
private String picturePath;
public LocalPicture(Parcel in) {
picturePath = in.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(picturePath);
}
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<LocalPicture> CREATOR = new Parcelable.Creator<LocalPicture>() {
public LocalPicture createFromParcel(Parcel in) {
return new LocalPicture(in);
}
public LocalPicture[] newArray(int size) {
return new LocalPicture[size];
}
};
}
I would go for writeParcelableArray, if you know the size won't change and *List if the size changes dynamically. Pass null as the classloader to use the base/default classloader.
I used read/writeList with getClass().getClassLoader()? That seems to do the job.
I have a working Parcelable implementaion for all the fields in my Parcelable Class apart from List<List<String>>
class Employee implements Parcelable {
List<List<String>> details;
//.......
protected Employee(Parcel in) {
details = new ArrayList<List<String>>();
// i know this is wrong just posting to clarify
in.readList(details, List.class.getClassLoader());
//......
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(details);
//.....
}
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<Employee> CREATOR =
new Parcelable.Creator<Employee>() {
public Employee createFromParcel(Parcel in) {
return new Employee(in);
}
public Employee[] newArray(int size) {
return new Employee[size];
}
};
}
Exception:
05-10 19:07:44.072: E/AndroidRuntime(10661): Caused by: java.lang.RuntimeException: Parcel android.os.Parcel#42a509e8: Unmarshalling unknown type code 3604535 at offset 268
Extending ArrayList and implementing Parcelable on it worked for me.
public class ParcelableArrayList extends ArrayList<String> implements
Parcelable {
private static final long serialVersionUID = -8516873361351845306L;
public ParcelableArrayList(){
super();
}
protected ParcelableArrayList(Parcel in) {
in.readList(this, String.class.getClassLoader());
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(this);
}
public static final Parcelable.Creator<ParcelableArrayList> CREATOR =
new Parcelable.Creator<ParcelableArrayList>() {
public ParcelableArrayList createFromParcel(Parcel in) {
return new ParcelableArrayList(in);
}
public ParcelableArrayList[] newArray(int size) {
return new ParcelableArrayList[size];
}
};
}
and Employee class
class Employee implements Parcelable {
List<ParcelableArrayList> details;
//.......
protected Employee(Parcel in) {
details = new ArrayList<ParcelableArrayList>();
in.readTypedList(details,ParcelableArrayList.CREATOR);
//......
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(details);
//.....
}
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<Employee> CREATOR =
new Parcelable.Creator<Employee>() {
public Employee createFromParcel(Parcel in) {
return new Employee(in);
}
public Employee[] newArray(int size) {
return new Employee[size];
}
};
}
I would create a class that extends List, implement Parcelable on that class. You can otherwise treat it as a normal list, but allow it to be parcelable.
Create class DetailsEntry implements Parcelable which contains List<String> and use List<DetailsEntry> details in Employee.