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.
Related
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
}
I am using Parcelable to communicate between fragments. Everything is working good, but sometimes when Android kills app process to free up the memory and user returns to app, the Parcelable gets huge amount of items from arrays like couple millions items instead of 2-3 and of course it throws OOM.
I suppose something's wrong with CREATOR, but I can't handle it.
The code for parcelable is composed from super class and child class and my interested item class:
base class:
public abstract class BaseRs implements Parcelable {
private String token;
private String msgAlert;
private String simpleMsg;
private AdsObj adsObj;
private Map<String, String> mapSettings;
//getters & setters
public BaseRs() {
}
protected BaseRs(Parcel in) {
token = in.readString();
msgAlert = in.readString();
simpleMsg = in.readString();
adsObj = in.readParcelable(AdsObj.class.getClassLoader());
mapSettings = MapParcelable.readParcelable(in);
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(token);
dest.writeString(msgAlert);
dest.writeString(simpleMsg);
dest.writeParcelable(adsObj, flags);
MapParcelable.writeToParcel(dest, mapSettings);
}
}
child class
public class GetSalesItemsRs extends BaseRs {
private SaleCoinItem[] coinPacksArray;
//getters+ setters
protected GetSalesItemsRs(Parcel in) {
super(in);
coinPacksArray = in.createTypedArray(SaleCoinItem.CREATOR);
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeTypedArray(coinPacksArray, flags);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<GetSalesItemsRs> CREATOR = new Creator<GetSalesItemsRs>() {
#Override
public GetSalesItemsRs createFromParcel(Parcel in) {
return new GetSalesItemsRs(in);
}
#Override
public GetSalesItemsRs[] newArray(int size) {
return new GetSalesItemsRs[size];
}
};
}
and my interested object
public class SaleCoinItem implements Parcelable {
private int amount;
private double price;
private int sortOrder;
//getters & setters
protected SaleCoinItem(Parcel in) {
amount = in.readInt();
price = in.readDouble();
sortOrder = in.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(amount);
dest.writeDouble(price);
dest.writeInt(sortOrder);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<SaleCoinItem> CREATOR = new Creator<SaleCoinItem>() {
#Override
public SaleCoinItem createFromParcel(Parcel in) {
return new SaleCoinItem(in);
}
#Override
public SaleCoinItem[] newArray(int size) {
return new SaleCoinItem[size];
}
};
}
The functiona to parcel Map - maybe here is the error ;)
public class MapParcelable {
public static void writeToParcel(Parcel out, Map<String, String> map) {
if (map != null) {
out.writeInt(map.size());
for (Map.Entry<String, String> entry : map.entrySet()) {
out.writeString(entry.getKey());
out.writeString(entry.getValue());
}
}
}
public static Map<String, String> readParcelable(Parcel parcel) {
Map<String, String> map = new HashMap<String, String>();
int size = parcel.readInt();
for (int i = 0; i < size; i++) {
String key = parcel.readString();
String value = parcel.readString();
map.put(key, value);
}
return map;
}
}
I get parcelable obj inside the onCreateView method :
GetSalesItemsRs mGetSalesItemsRs = getArguments().getParcelable(KEY_PARCEL);
I think the problem is that if the Map is null you're not writing the size field to the Parcel, but when you read it you always read the size field, and it might end up reading some random value. So or you change the code to
if (map != null) {
out.writeInt(map.size());
for (Map.Entry<String, String> entry : map.entrySet()) {
out.writeString(entry.getKey());
out.writeString(entry.getValue());
}
}else{
out.writeInt(0);
}
and you will never get a null value when recreating the class, or you create another boolean field "hasMap" and always write that one
I have a class with the following structure:
class Example {
Integer age;
String name;
Collection<Example2>examples;
class Example2 {
Integer number;
Collection<String>strings;
}
}
How can I make this class implement Parcelable so that I can send its object across activities.
The simplest way will be to use #AutoParcel and let it handle the heavy lifting for you.
You should take a look at this thread : Android: Making a class parcelable
So your classes should be something like :
class Example implements Parcelable {
Integer age;
String name;
ArrayList<Example2> examples;
public Example(Parcel in) {
examples = new ArrayList<>();
readFromParcel(in);
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeString(name);
dest.writeList(examples != null ? examples : new ArrayList());
}
private void readFromParcel(Parcel in) {
age = in.readInt();
name = in.readString();
in.readList(examples, Example2.class.getClassLoader());
}
public static final Creator<Example> CREATOR = new Creator<Example>() {
#Override
public Example createFromParcel(Parcel in) {
return new Example(in);
}
#Override
public Example[] newArray(int size) {
return new Example[size];
}
};
}
and
class Example2 implements Parcelable {
Integer number;
ArrayList<String> labels;
public Example2(Parcel in) {
labels = new ArrayList<>();
readFromParcel(in);
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInteger(number);
dest.writeList(labels != null ? labels : new ArrayList());
}
#Override
public int describeContents() {
return 0;
}
private void readFromParcel(Parcel in) {
number = in.readInteger();
in.readStringList(labels);
}
public static final Creator<Example2> CREATOR = new Creator<Example2>() {
#Override
public Example2 createFromParcel(Parcel in) {
return new Example2(in);
}
#Override
public Example2[] newArray(int size) {
return new Example2[size];
}
};
}
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();
}
}
I could not find any solution for how to treat with integer array in case of parcel (I want to use these two functions dest.writeIntArray(storeId); and in.readIntArray(storeId);).
Here is my code
public class ResponseWholeAppData implements Parcelable {
private int storeId[];
public int[] getStoreId() {
return storeId;
}
public void setStoreId(int[] storeId) {
this.storeId = storeId;
}
#Override
public int describeContents() {
return 0;
}
public ResponseWholeAppData(){
storeId = new int[2];
storeId[0] = 5;
storeId[1] = 10;
}
public ResponseWholeAppData(Parcel in) {
if(in.readByte() == (byte)1)
in.readIntArray(storeId); //how to do this storeId=in.readIntArray(); ?
}
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if(storeId!=null&&storeId.length>0)
{
dest.writeByte((byte)1);
dest.writeIntArray(storeId);
}
else
dest.writeByte((byte)0);
}
public static Parcelable.Creator<ResponseWholeAppData> getCreator() {
return CREATOR;
}
public static void setCreator(Parcelable.Creator<ResponseWholeAppData> creator) {
CREATOR = creator;
}
public static Parcelable.Creator<ResponseWholeAppData> CREATOR = new Parcelable.Creator<ResponseWholeAppData>()
{
public ResponseWholeAppData createFromParcel(Parcel in)
{
return new ResponseWholeAppData(in);
}
public ResponseWholeAppData[] newArray(int size)
{
return new ResponseWholeAppData[size];
}
};
}
When I use "in.readIntArray(storeID)", I get an error:
"Caused by: java.lang.NullPointerException
at android.os.Parcel.readIntArray(Parcel.java:672)".
Instead of using "readIntArray" I used the following:
storeID = in.createIntArray();
Now there are no errors.
I assume class MyObj implements Parcelable and implements all the required methods; I will suggest here only the details about reading / writing parcel.
If array size is known beforehand:
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeIntArray(mMyIntArray); // In this example array length is 4
}
protected MyObj(Parcel in) {
super(in);
mMyIntArray = new int[4];
in.readIntArray(mMyIntArray);
}
Otherwise:
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(mMyArray.length); // First write array length
out.writeIntArray(mMyIntArray); // Then array content
}
protected MyObj(Parcel in) {
super(in);
mMyIntArray = new int[in.readInt()];
in.readIntArray(mMyIntArray);
}