I have the following models:
GitItem:
public class GitItem implements Serializable, Parcelable {
public int id;
public String name;
public String full_name;
public GitOwner owner;
public GitItem() {
}
public GitItem(Parcel in) {
id = in.readInt();
name = in.readString();
full_name = in.readString();
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags){
out.writeInt(id);
out.writeString(name);
out.writeString(full_name);
}
public static final Parcelable.Creator<GitItem> CREATOR = new Parcelable.Creator<GitItem>(){
#Override
public GitItem createFromParcel(Parcel source) {
return new GitItem(source);
}
#Override
public GitItem[] newArray(int size) {
return new GitItem[size];
}
};
}
and GitOwner:
public class GitOwner implements Serializable, Parcelable {
public String avatar_url;
public GitOwner(Parcel in) {
avatar_url = in.readString();
}
public GitOwner() {
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
}
public static final Parcelable.Creator<GitOwner> CREATOR = new Parcelable.Creator<GitOwner>(){
#Override
public GitOwner createFromParcel(Parcel source) {
return new GitOwner(source);
}
#Override
public GitOwner[] newArray(int size) {
return new GitOwner[size];
}
};
}
and then in MainActivity.java I have a List<GitItem> data; which is bound to my RecyclerView.
Basically what I am trying to achieve, is to store current state of my List<GitItem> when I change Activity or simply rotate device. Unfortunately, even though I have implemented Parcelable (as suggested by other posts), I was unable to store the instance.
Any help in this matter would be highly appreciated.
You have not implemented the complete body of Parcelable functions i.e writeToParcel in GitOwner and GitItem class
In GitOwner class
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(avatar_url);
// add this line
}
and inside GitItem use writeParcelable to write and readParcelable to read GitItem object otherwise it won't be persisted and restored
public GitItem(Parcel in) {
id = in.readInt();
name = in.readString();
full_name = in.readString();
owner = in.readParcelable(GitItem.class.getClassLoader());
// Add line to read owner object
}
public void writeToParcel(Parcel out, int flags){
out.writeInt(id);
out.writeString(name);
out.writeString(full_name);
out.writeParcelable(owner , flags);
// Add line to write owner object
}
Related
I have a Parcelable object that has a list of Parcelable objects. I am trying to read that list back after it has been passed from one Activity to the next, but only the first element is "un-bundled"
public class MyBundle implements Parcelable {
private List<Data> dataList;
public static final Parcelable.Creator<MyBundle> CREATOR = new Parcelable.Creator<MyBundle>() {
public MyBundle createFromParcel(Parcel in) {
return new MyBundle(in);
}
public MyBundle[] newArray(int size) {
return new MyBundle[size];
}
};
public MyBundle() {
}
public MyBundle(Parcel in) {
//dataList = new ArrayList<>();
//in.readTypedList(dataList, Data.CREATOR);
dataList = in.createTypedArrayList(Data.CREATOR);
//BOTH have the same result
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if (dataList != null && dataList.size() > 0) {
dest.writeTypedList(dataList);
}
}
}
The data object:
/*BaseObject has the following properties:
UUID uuid;
long databaseId;
createdDate;
modifiedDate;
*/
public class Data extends BaseObject implements Parcelable {
private String name;
private String serial;
private String location;
public Data() {}
private Data(Parcel in) {
String uuidString = in.readString();
if (uuidString == null) return; //this is null!
uuid = UUID.fromString(idString);
databaseId = in.readLong();
createdDate = new Date(in.readLong());
modifiedDate = new Date(in.readLong());
location = in.readString();
name = in.readString();
serial = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(uuid.toString());
dest.writeLong(databaseId);
dest.writeLong(createdDate.getTime());
dest.writeLong(modifiedDate.getTime());
dest.writeString(name);
dest.writeString(serial);
}
public static final Parcelable.Creator<Data> CREATOR
= new Parcelable.Creator<Data>() {
public Data createFromParcel(Parcel in) {
return new Data(in);
}
public Data[] newArray(int size) {
return new Data[size];
}
};
}
What I have tried:
Debugging - I can see the first element is read fine but the rest are return null, and they do have values when they are being written
"Android, How to use readTypedList method correctly in a Parcelable class?"
"how to properly implement Parcelable with an ArrayList?"
So this is the answer: My Data parcelable misses the location element when it creates the parcel. This obviously results in some kind of offset error when READING occurs. So the coded solution is as follows:
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(uuid.toString());
dest.writeLong(databaseId);
dest.writeLong(createdDate.getTime());
dest.writeLong(modifiedDate.getTime());
dest.writeString(location); /*HERE!*/
dest.writeString(name);
dest.writeString(serial);
}
I hope this helps someone else.
I'm trying to do a Parcelable Object to pass data between activities. With the primary variables I don't have issues, but now I need to pass an array of another object, and I don't know how to do it. I'm searching for some examples, but I didn't find a similar case to my problem.
This is my source code of the principal object:
public class Quiz implements Parcelable {
int event_id;
String name;
String business_unit;
int functional_area_business; // 0 unchecked, 1 checked
int functional_area_support; // 0 unchecked, 1 checked
Answer [] list_answers;
//GETTERS AND SETTERS
...
//
public static final Parcelable.Creator<Quiz> CREATOR = new Parcelable.Creator<Quiz>() {
public Quiz createFromParcel(Parcel in) {
return new Quiz(in);
}
public Quiz[] newArray(int size) {
return new Quiz[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(event_id);
dest.writeString(name);
dest.writeString(business_unit);
dest.writeInt(functional_area_business);
dest.writeInt(functional_area_support);
dest.writeArray(list_answers);
}
private Quiz(Parcel in) {
event_id = in.readInt();
name = in.readString();
business_unit = in.readString();
functional_area_business = in.readInt();
functional_area_support = in.readInt();
}
}
The other class is the next.
public class Answer {
int answer_id;
String value;
}
If someone could tell me what I need to modify, I will be gratefully.
Use an ArrayList instead of an Array and make Answer class Parceable then update your code to the following:
public class Quiz implements Parcelable {
int event_id;
String name;
String business_unit;
int functional_area_business; // 0 unchecked, 1 checked
int functional_area_support; // 0 unchecked, 1 checked
List<Answer > list_answers;
//GETTERS AND SETTERS
...
//
public static final Parcelable.Creator<Quiz> CREATOR = new Parcelable.Creator<Quiz>() {
public Quiz createFromParcel(Parcel in) {
return new Quiz(in);
}
public Quiz[] newArray(int size) {
return new Quiz[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(event_id);
dest.writeString(name);
dest.writeString(business_unit);
dest.writeInt(functional_area_business);
dest.writeInt(functional_area_support);
dest.writeTypedList(list_answers);
}
private Quiz(Parcel in) {
event_id = in.readInt();
name = in.readString();
business_unit = in.readString();
functional_area_business = in.readInt();
functional_area_support = in.readInt();
list_answers = new ArrayList<Answer>();
in.readTypedList(list_answers , Answer.CREATOR);
}
}
And this is the Parceable Answer:
public class Answer implements Parcelable {
int answer_id;
String value;
public static final Parcelable.Creator<Answer> CREATOR = new Parcelable.Creator<Answer>() {
public Answer createFromParcel(Parcel in) {
return new Answer(in);
}
public Answer[] newArray(int size) {
return new Answer[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(answer_id);
dest.writeString(value);
}
private Answer(Parcel in) {
answer_id = in.readInt();
value = in.readString();
}
}
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'm trying to parcel an object which contains some string/int variables and an object variable. The strings and int's are working, but not the nested object. I understand I would have to it also parcelable, but I'm apparently doing something wrong=. In my nested class, the writeToParcel method gets called (I check with a Log.d() call), but the createFromParcel() doesn't. I end up getting a null object. This is my simplified code:
public class MyClass implements Parcelable {
public MyClass() {
}
private Integer id;
private String name;
private MyOtherClass otherClass = new MyOtherClass();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public OtherClass getOtherClass() {
return otherClass;
}
public void setOtherClass(OtherClass otherClass) {
this.otherClass = otherClass;
}
public static final Parcelable.Creator<MyClass> CREATOR
= new Parcelable.Creator<MyClass>() {
public MyClass createFromParcel(Parcel in) {
return new MyClass(in);
}
public MyClass[] newArray(int size) {
return new MyClass[size];
}
};
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(id);
dest.writeParcelable(otherClass, flags);
}
private MyClass(Parcel in) {
name = in.readString();
id = in.readInt();
otherClass = (OtherClass) in.readParcelable(OtherClass.class.getClassLoader());
}
}
class MyOtherClass implements Parcelable {
public OtherClass() {
}
private String resourcePreviewURL;
public String getResourcePreviewURL() {
return resourcePreviewURL;
}
public void setResourcePreviewURL(String resourcePreviewURL) {
this.resourcePreviewURL = resourcePreviewURL;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
Log.d("parcel", "write to parcel"); // this gets called
out.writeString(resourcePreviewURL);
}
public static final Parcelable.Creator<MyOtherClass> CREATOR
= new Parcelable.Creator<MyOtherClass>() {
public MyOtherClass createFromParcel(Parcel in) {
Log.d("parcel", "create from parcel"); // this doesn't get called
return new MyOtherClass(in);
}
public ResourcePreviews[] newArray(int size) {
return new ResourcePreviews[size];
}
};
private OtherClass(Parcel in) {
Log.d("parcel", "read from parcel"); // this doesn't get called
resourcePreviewURL = in.readString();
}
}
I solved this by changing the order in which I write/read from the Parcel. I made the dest.writeParcelable(otherClass, flags); and the otherClass = (OtherClass) in.readParcelable(OtherClass.class.getClassLoader()); calls to be the first in their methods and it started working. Is that an issue?
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(otherClass, flags);
dest.writeString(name);
dest.writeInt(id);
}
private MyClass(Parcel in) {
otherClass = (OtherClass) in.readParcelable(OtherClass.class.getClassLoader());
name = in.readString();
id = in.readInt();
}