Parcelable is really usefull? - java

I tried to send a parcel from the mainActivity to a fragment and i noticed a strange effect.
When you implement parcelable, android require to implement "writeToParcel" and "describeContents".
The strange effect is, if you write something inside "writeToParcel" or nothing, it doesn't matter. Android never call it.
Why these functions are required ?
Here is a working source code :
public class Movie implements Parcelable {
private String title;
private Float score;
public Movie(String title, float score) {
this.title = title;
this.score = score;
}
...... Getter and Setter......
//Parcelable Methods
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
// Don't need to write anything in this area
/*
dest.writeString(title);
dest.writeFloat(score);
*/
}
public static final Parcelable.Creator<Movie> CREATOR = new Parcelable.Creator<Movie>() {
//--------------> You can return null, it's never called and work perfectly!
public Movie createFromParcel(Parcel in) {
return null;//new Movie(in);
}
public Movie[] newArray(int size) {
return null;//new Movie[size];
}
};
}
Any idear ?
Regards,

Related

How to pass a Parcelable class which contains a list of another parcelable class?

I'm trying to pass a Parcelable class from one Activity to another. I do it like this:
Intent intent = new Intent(ClosedChatActivity.this, AdminProfileActivity.class);intent.putExtra("adminProfile", adminProfile);
startActivity(intent);
And then get it in the other Activity like this:
adminProfile = (AdminProfile) getIntent().getExtras().getParcelable("adminProfile");
This is the AdminProfile class and the WebLink class it has inside:
public class AdminProfile implements Parcelable {
public static final Creator<AdminProfile> CREATOR = new Creator<AdminProfile>() {
#Override
public AdminProfile createFromParcel(Parcel in) {
return new AdminProfile(in);
}
#Override
public AdminProfile[] newArray(int size) {
return new AdminProfile[size];
}
};
public Long idUser;
public String name;
public String professio;
public String description;
public List<WebLink> webLinks;
public Long idOficina;
protected AdminProfile(Parcel in) {
idUser = in.readLong();
name = in.readString();
professio = in.readString();
description = in.readString();
webLinks = in.createTypedArrayList(WebLink.CREATOR);
idOficina = in.readLong();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(idUser);
parcel.writeString(name);
parcel.writeString(professio);
parcel.writeString(description);
parcel.writeLong(idOficina);
parcel.writeTypedList(webLinks);
}
}
public class WebLink implements Parcelable {
public static final Creator<WebLink> CREATOR = new Creator<WebLink>() {
#Override
public WebLink createFromParcel(Parcel in) {
return new WebLink(in);
}
#Override
public WebLink[] newArray(int size) {
return new WebLink[size];
}
};
public String name;
public String url;
protected WebLink(Parcel in) {
name = in.readString();
url = in.readString();
}
#Override
public boolean equals(#Nullable Object obj) {
WebLink webLink = (WebLink) obj;
assert webLink != null;
return this.name.equals(webLink.name) && this.url.equals(webLink.url);
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
parcel.writeString(url);
}
}
When I get the object, the two Strings in it aren't the same I sent. Why is that?
The weird thing is that I have another Parcelable class which contains an AdminProfile, and when sending that class it does send fine with the Weblinks included, but if I send only an AdminProfile somewhere in the way it fails.
When you read and write Parcelable classes, you need to make sure that the order of the elements is exactly the same.
You are writing idOficina followed by WebLinks, but you are reading them in the opposite order.

How to pass objects within objects between activities with parcelable

Im pretty new in Android Studio.
I'm trying to pass an ArrayList from one activity to another using parcelable. Within the class Recipe I declare another ArrayList which I cannot get a hold of when starting the other activity.
Recipe.java:
public class Recipe implements Parcelable {
String name;
ArrayList<Ingredient> ingredients;
public Recipe(String name){
this.name = name;
this.ingredients = new ArrayList<>();
}
protected Recipe(Parcel in) {
name = in.readString();
}
public static final Creator<Recipe> CREATOR = new Creator<Recipe>() {
#Override
public Recipe createFromParcel(Parcel in) {
return new Recipe(in);
}
#Override
public Recipe[] newArray(int size) {
return new Recipe[size];
}
};
public void addIngredients(String[] amountList, String[] ingredientList, String[] unitList) {
for (int i = 0; i < ingredientList.length; i++) {
ingredients.add(new Ingredient(ingredientList[i], Double.parseDouble(amountList[i]), unitList[i]));
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
}
}
Ingredient.java:
public class Ingredient implements Parcelable {
private String ingrdnt;
private double amount;
private String unit;
private String cat;
private boolean checkedItem;
public Ingredient(String ingrdnt, double amount, String unit) {
this.ingrdnt = ingrdnt;
this.amount = amount;
this.unit = unit;
//this.cat = category;
this.checkedItem = false;
}
protected Ingredient(Parcel in) {
ingrdnt = in.readString();
amount = in.readDouble();
unit = in.readString();
cat = in.readString();
checkedItem = in.readByte() != 0;
}
public static final Creator<Ingredient> CREATOR = new Creator<Ingredient>() {
#Override
public Ingredient createFromParcel(Parcel in) {
return new Ingredient(in);
}
#Override
public Ingredient[] newArray(int size) {
return new Ingredient[size];
}
};
public double getAmount() {
return amount;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(ingrdnt);
parcel.writeDouble(amount);
parcel.writeString(unit);
parcel.writeString(cat);
parcel.writeByte((byte) (checkedItem ? 1 : 0));
}
}
In main:
private ArrayList<Recipe> recipes = new ArrayList<>();
//recipes obviously holds a bunch of recipes so it's not empty.
intent.putExtra("recipes", recipes);
System.out.println(recipes.get(0).ingredients.get(0).getAmount());
System.out: 2.0
In second activity:
recipes = this.getIntent().getParcelableArrayListExtra("recipes");
//Same print as above
System.out.println(recipes.get(0).ingredients.get(0).getAmount());
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference
Have I implemented the parcelable in a wrong way or why can I not get a hold of the Ingredient objects?
I've read about other ways to pass objects between activities but it seems like parcelable might be the best way to do it.
Yes, you basically forgot to write the ingredients of the Recipe to the output Parcel which is given to the Recipe.writeToParcel method.
You can write the ArrayList<Parcelable> with writeTypedList and read it back with readTypedList.
So your Recipe constructor which accepts a Parcel should be like:
protected Recipe(Parcel in) {
name = in.readString();
ingredients = new ArrayList<>();
in.readTypedList(ingredients, Ingredient.CREATOR);
}
while your writeToParcel of the Recipe should become:
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
parcel.writeTypedList(ingredients);
}
The NullPointerException you are seeing is caused by the fact that you do not allocate a new ArrayList in the constructor of Recipe which accepts a Parcel, so when you call recipes.get(0).ingredients.get(0).getAmount() in the second Activity the ingredients ArrayList is null, thus the Exception.
Also note (but not related to the problem) that there exist a writeBoolean and a readBoolean with which you can write and read values of type boolean (I am saying this for the Ingredient class implementation).
Try those out and let us know if it worked properly.

Use of ClassLoader in parcelable.readArrayList() in Android

I've got a Class named as FilterData which implements Parcelable. I had a member variable private ArrayList<String> listPropertyType; When implementing the parcelable interface in my class, parcel.readArrayList(null) ,the parameter is shown as ClassLoader object. With this member variable the FilterData class works as intended. But I wanted to implement a scenario where ClassLoader object is passed to readArrayList() method.
So what I've gathered from the documentation which is unclear about the public ArrayList readArrayList (ClassLoader loader) that if the ArrayList contains non primitive class ,we have to use ClassLoader.
What's the use case in this scenerio of using a ClassLoader and how to use it? I wanted to use the ClassLoader in the following matter. Added a member variable private ArrayList<RandomClass> listRandom; RandomClass randomClass; to implement this.
My FilterData class holds :
public FilterData(Parcel parcel)
{
listPropertyType=parcel.readArrayList(null);
listRandom=parcel.readArrayList(randomClass);
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeList(listPropertyType);
parcel.writeList(listRandom);
}
And my RandomClass is :
public class RandomClass extends ClassLoader{
//this class is for testing classloader in ArrayList in parcelable .readArrayList()
String name;
int age;
public RandomClass(String name, int age) {
this.name = name;
this.age = age;
}
}
This implementation doesn't work. So how to use this?
First of all, if we want to get a class Parcelable, we can use a website www.parcelabler.com. It will generate the syntax of the class. Secondly If we wanted to use an ArrayList of a class of our own. Making a Parcelable class containing the ArrayList won't work. We need to make the custom class Parcelable as well.
If a class that needs to be parcelable contains a member variable ArrayList<String> list; , the class which is generated from the website, should be :
public class MyParcelable implements Parcelable {
ArrayList<String> list;
public MyParcelable(ArrayList<String> list) {
this.list = list;
}
protected MyParcelable(Parcel in) {
if (in.readByte() == 0x01) {
list = new ArrayList<String>();
in.readList(list, String.class.getClassLoader());
} else {
list = null;
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if (list == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(list);
}
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
#Override
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
#Override
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
}
And if the Class contains an ArrayList of a class made by us like this ArrayList<DummyClass> list; , the code should be:
public class MyComplexParcelable implements Parcelable {
ArrayList<DummyClass> list;
public MyComplexParcelable(ArrayList<DummyClass> list) {
this.list = list;
}
#Override
public String toString() {
return "MyComplexParcelable{" +
"list=" + list +
'}';
}
protected MyComplexParcelable(Parcel in) {
if (in.readByte() == 0x01) {
list = new ArrayList<DummyClass>();
in.readList(list, DummyClass.class.getClassLoader());
} else {
list = null;
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if (list == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(list);
}
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<MyComplexParcelable> CREATOR = new Parcelable.Creator<MyComplexParcelable>() {
#Override
public MyComplexParcelable createFromParcel(Parcel in) {
return new MyComplexParcelable(in);
}
#Override
public MyComplexParcelable[] newArray(int size) {
return new MyComplexParcelable[size];
}
};
}
If we compare both cases , there is no difference in the syntax. Both class uses in.readList(list, String.class.getClassLoader()); and in.readList(list, DummyClass.class.getClassLoader()); which uses ClassLoader object of the correspondent classes.
But in the latter case, we need to make the DummyClass parcelable. Like this:
public class DummyClass implements Parcelable {
int age;
String name;
public DummyClass(int age, String name) {
this.age = age;
this.name = name;
}
#Override
public String toString() {
return "DummyClass{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
protected DummyClass(Parcel in) {
age = in.readInt();
name = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeString(name);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<DummyClass> CREATOR = new Parcelable.Creator<DummyClass>() {
#Override
public DummyClass createFromParcel(Parcel in) {
return new DummyClass(in);
}
#Override
public DummyClass[] newArray(int size) {
return new DummyClass[size];
}
};
}
If the array list you're reading from the parcel is subclass you've made, and not a standard java list subclass, or if it contains non-primitive types, you need to pass MyCustomList.class.getClassLoader().
You need to do it this way because parcelables can be inflated everywhere, anytime, so the code must know the ClassLoader required to load it. Most of the time you won't need it because you will be parcelling stuff inside your own app, but it is a good practice.
In fact, you said the list is a simple ArrayList<String>, so you don't need to do anything. It would be different if it was a MyListSubclass<String>, a ArrayList<MyObject>, or an ArrayList<HashMap<Object, Object>> that may contain other subclassed objects. You get the idea.

Open Pdf at Previous Page Number where it user left

I am tring to open the recent pdf file at page number from firebase How to Pick Up Reading Where User Left it.
I am Creating a Model Class of Mutiple PDF book and fetch online.
how i can open the recent page that i left in firebase.
public class ModelPdf implements Parcelable {
private String images;
private String name;
private String url;
public ModelPdf() {
}
public ModelPdf(String images, String name, String url) {
//parameterized constructor
}
protected ModelPdf(Parcel in) {
images = in.readString();
name = in.readString();
url = in.readString();
}
public static final Creator<ModelPdf> CREATOR = new Creator<ModelPdf>() {
#Override
public ModelPdf createFromParcel(Parcel in) {
return new ModelPdf(in);
}
#Override
public ModelPdf[] newArray(int size) {
return new ModelPdf[size];
}
};
// rest getter and setter methods of class attribute
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(images);
dest.writeString(name);
dest.writeString(url);
}
}
You can use SharedPreferences to store current page value and then use it.

putParcelableArrayListExtra and black screen

I have custom class that implements Parcelable and I use it as custom arraylist.
When I use putParcelableArrayListExtra and 400 rows it works fine, but 1000 rows it does not. I have black screen and app locks up. What is wrong?
EDIT:
I sent it here and I don't use it in another Activity.
Intent intent = new Intent().setClass(getApplicationContext(), ArtActivity.class);
intent.putParcelableArrayListExtra ("mylist", list);
startActivityForResult(intent, SECONDARY_ACTIVITY_REQUEST_CODE);
My array:
ArrayList<Piece> list = new ArrayList<Piece>();
It is my Class:
public class Piece implements Parcelable {
private String id;
private String name;
private int type;
private String text;
private String mp3;
public Piece (String id,String name,int type)
{
this.id=id;
this.name=name;
this.type=type;
}
public Piece(Piece ele)
{
this.id=ele.id;
this.name=ele.name;
this.type=ele.type;
this.text=ele.text;
}
public Piece (Parcel in)
{
id = in.readString ();
name = in.readString ();
type = in.readInt();
text= in.readString();
mp3=in.readString();
}
public static final Parcelable.Creator<Piece> CREATOR
= new Parcelable.Creator<Piece>()
{
public Piece createFromParcel(Parcel in)
{
return new Piece(in);
}
public Piece[] newArray (int size)
{
return new Piece[size];
}
};
public void makeText(String text)
{
this.text=text;
}
public void makeMp3(String mp3)
{
this.mp3= mp3;
}
public String getMp3()
{
return this.mp3;
}
public String getId()
{
return id;
}
public String getName()
{
return name;
}
public int getType()
{
return type;
}
public String getText()
{
return text;
}
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeString (id);
dest.writeString (name);
dest.writeInt(type);
dest.writeString (text);
dest.writeString (mp3);
}
}
I do not believe you should be using parcelable in this case. I would either access the data statically (if you only intend to have one persistent instance of the data), or use a caching system to hold onto the data.
This is an example of a publicly available static variable:
public static List<Piece> list;
It is accessible from everywhere in your app that has visibility of the class.
However, doing this is very messy and is considered a bad practice. Alternatively, you can create an object to manage the data for you as a static class or singleton:
public class MyListManager {
private static List<Piece> mList;
public static List<Piece> getMyList() {
return mList;
}
public static void setList(List<Piece> list) {
mList = list;
}
}
Alternatively, you can implement some kind of a caching system to manage your data.

Categories