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.
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 have browsed the question on Stack and I found no question facing the particular problem. I have a class with two arraylist of custom objects. I am not sure how to implement writeToParcel for the parent class now. One option I found were readTypedList constructor, but I cant implement it twice. There is no readTypedListArray constructor that I can create an array ans send it.
Just a reminder the object are ArrayList and there are two such objects in the parent class. The github link for reference is here.
https://github.com/legalimpurity/PopularMoviesStage2/tree/master/app/src/main/java/popularmoviesstage1/legalimpurity/com/popularmoviesstage2/objects
You can add customArray using following way :
In MovieObject class:
#Expose
private ArrayList< ReviewObject > ReviewObjs = new ArrayList< ReviewObject >();
private ArrayList< TrailerVideoObject > TrailerVideoObjs = new ArrayList< TrailerVideoObject >();
writeToParcel() :
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(ReviewObjs);
dest.writeTypedList(TrailerVideoObjs);
}
and readFromParcel() :
private void readFromParcel(Parcel in) {
ReviewObjs = new ArrayList<ReviewObject>();
in.readTypedList(ReviewObjs, ReviewObject.CREATOR);
TrailerVideoObjs = new ArrayList<TrailerVideoObject>();
in.readTypedList(TrailerVideoObjs, TrailerVideoObject.CREATOR);
}
And add following lines in CustomClasses :
for ReviewObject :
public class ReviewObject implements Parcelable {
public static final Creator<ReviewObject> CREATOR = new Creator<ReviewObject>() {
public ReviewObject createFromParcel(Parcel in) {
return new ReviewObject(in);
}
public ReviewObject[] newArray(int size) {
return new ReviewObject[size];
}
};
// add other objects...
}
for TrailerVideoObject :
public class TrailerVideoObject implements Parcelable {
public static final Creator<TrailerVideoObject> CREATOR = new Creator<TrailerVideoObject() {
public TrailerVideoObject createFromParcel(Parcel in) {
return new TrailerVideoObject(in);
}
public TrailerVideoObject[] newArray(int size) {
return new TrailerVideoObject[size];
}
};
// add other objects...
}
You can try below workaround in MovieObject.java
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(new String[] {
this.OrignalTitle,
this.MoviePosterImageThumbnailUrl,
this.PlotSynopsis,
this.UserRating,
this.ReleaseDate
});
dest.writeLongArray(new long[] {
this._id,
this.ApiId
});
ArrayList<ArrayList> temp = new List<>();
temp.add(ReviewObjs);
temp.add(TrailerVideoObjs);
dest.writeTypedList(temp);
}
In your MovieObject.java, you can declare them as List<YOUR_CLASS> instead of ArrayList<YOUR_CLASS>.
In my code:
public class CombinedClass implements Parcelable {
private List<Class1> class1List;
private List<Class2> class2List;
//Constructor, getter, setter, methods.
}
Both Class1 and Class2 implements Parcelable too.
I'm using "auto-generated" Parcelable implementation provided by Android Studio.
Here is the Parcelable implementation:
protected CombinedClass(Parcel in) {
class1List = in.createTypedArrayList(Class1.CREATOR);
class2List = in.createTypedArrayList(Class2.CREATOR);
}
public static final Creator<CombinedClass> CREATOR = new Creator<CombinedClass>() {
#Override
public CombinedClass createFromParcel(Parcel in) {
return new CombinedClass(in);
}
#Override
public CombinedClass[] newArray(int size) {
return new CombinedClass[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeTypedList(class1List);
parcel.writeTypedList(class2List);
}
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];
}
};
}
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 class that contains data i want to pass it between intents, this class have arraylist that contains another class objects. this is my class
public class ParsedData implements Parcelable {
public String error;
public float protectionLevel;
public int protectionLevelColor;
public double lastBackup;
public boolean compressedType;
public Long driveFreeSpaceSize;
ArrayList<Item>Items = new ArrayList<Item>();
}
class Item {
public String name;
public float percentage;
public int files;
public long size;
}
how can i send this class between intents ?
you can let your class Item implements the Serializable interface, and use Intent.putExtra(String, Serializable). Since ArrayList implements also the Serializable interface, you can pass the whole Items object.
This may be your problem:
Classes implementing the Parcelable interface must also have a static field called CREATOR, which is an object implementing the Parcelable.Creator interface.
Alternatively, I'd try to have Item implement Parcelable, as well.
The fail-safe alternative is to write your data structure into a JSON string, which also allows you to pass the data to other applications that don't have access to your ParsedData class.
You may take a look at Intent.putExtra(String name, Parcelable object) and implement the parcelable interface in your class.
i have found the answer after all. thanks all how helped me
this is the answer:
import android.os.Parcel;
import android.os.Parcelable;
public class ParsedData implements Parcelable {
public String error;
public float protectionLevel;
public int protectionLevelColor;
public double lastBackup;
public boolean compressedType;
public Long statusSendTime;
ArrayList<Item>Items = new ArrayList<Item>();
//---------------------Constructors---------------------------
public ParsedData() { ; };
public ParsedData(Parcel in) {
readFromParcel(in);
}
//------------------------------------------------------------
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(error);
dest.writeFloat(protectionLevel);
dest.writeInt(protectionLevelColor);
dest.writeDouble(lastBackup);
dest.writeByte((byte) (compressedType ? 1 : 0));
dest.writeLong(statusSendTime);
dest.writeList(Items);
}
private void readFromParcel(Parcel in) {
error = in.readString();
protectionLevel = in.readFloat();
protectionLevelColor = in.readInt();
lastBackup = in.readDouble();
compressedType =in.readByte() == 1;
statusSendTime = in.readLong();
in.readList(Items,Item.class.getClassLoader() );
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public ParsedData createFromParcel(Parcel in) {
return new ParsedData(in);
}
public ParsedData[] newArray(int size) {
return new ParsedData[size];
}
};
}
class Item implements Parcelable {
public String name;
public float percentage;
//---------------------Constructors---------------------------
public Item() {
}
public Item(Parcel in) {
readFromParcel(in);
}
//------------------------------------------------------------
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeFloat(percentage);
}
public static final Creator<Item> CREATOR = new Creator<Item>() {
public Item createFromParcel(Parcel source) {
return new Item(source);
}
public Item[] newArray(int size) {
return new Item[size];
}
};
private void readFromParcel(Parcel in) {
this.name = in.readString();
this.percentage = in.readFloat();
}
}
and in the caller activity
ParsedData data = new PArsedData();
Intent intentBreakDown = new Intent(this,BreakDownBarActivity.class);
intentBreakDown.putExtra("data", data);
startActivity(intentBreakDown);
in the called activity(BreakDownBarActivity in my case)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.breakdownbar);
Bundle b = getIntent().getExtras();
ParsedData data = (ParsedData)b.getParcelable("data");
}