Pass array in Intent using parcelable - android

I would like to send an array of objects between activities. I want to use the parcelable interface and send the data in an intent. However I keep getting errors. I have been stuck for 2 days. Here are some details about my problem.
Class A
private ProjetUI[] mProjects;
private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
Context context = view.getContext();
Intent intent = new Intent(context, ProjetListActivity.class);
intent.putExtra(ProjetListActivity.ARG_PROJECTS, mProjects);
context.startActivity(intent);
}
};
Class B
ProjetUI[] mProjects = getIntent().getParcelableArrayExtra(ARG_PROJECTS);
I get a compilation error "Incompatible types"
After casting to (ProjetUI[]), I get a runtime error "Cannot cast Parcelable[] to ProjetUI[]"
Class Projet
public class ProjetUI implements Parcelable {
private String id;
private String idParent;
private String nom;
private String description;
private List<ProjetColonneUI> colonnes;
private List<VueUI> vues;
private boolean archive;
private String version;
private String commentaire;
private boolean published;
private List<DroitAccesUI> droitAcces;
private String idDossier;
private String typeDossier;
private String idModele;
private List<ProjetDatasetUI> projetDatasets;
protected ProjetUI(Parcel in) {
id = in.readString();
idParent = in.readString();
nom = in.readString();
description = in.readString();
colonnes = in.createTypedArrayList(ProjetColonneUI.CREATOR);
vues = in.createTypedArrayList(VueUI.CREATOR);
archive = in.readInt() == 1;
version = in.readString();
commentaire = in.readString();
published = in.readInt() == 1;
droitAcces = in.createTypedArrayList(DroitAccesUI.CREATOR);
idDossier = in.readString();
typeDossier = in.readString();
idModele = in.readString();
projetDatasets = in.createTypedArrayList(ProjetDatasetUI.CREATOR);
}
public static final Creator<ProjetUI> CREATOR = new Creator<ProjetUI>() {
#Override
public ProjetUI createFromParcel(Parcel in) {
return new ProjetUI(in);
}
#Override
public ProjetUI[] newArray(int size) {
return new ProjetUI[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(getId());
parcel.writeString(getIdParent());
parcel.writeString(getNom());
parcel.writeString(getDescription());
parcel.writeTypedList(getColonnes());
parcel.writeTypedList(getVues());
parcel.writeInt(isArchive() ? 1 : 0);
parcel.writeString(getVersion());
parcel.writeString(getCommentaire());
parcel.writeInt(isPublished() ? 1 : 0);
parcel.writeTypedList(getDroitAcces());
parcel.writeString(getIdDossier());
parcel.writeString(getTypeDossier());
parcel.writeString(getIdModele());
parcel.writeTypedList(getProjetDatasets());
}
}
EDIT
This is the complete stacktrace
The other classes implement parcelable just like ProjeUI class.
Here is an example of another class that has an enum type and an example of an enum that implements parcelable
public class VueRelationUI implements Parcelable {
private String id;
private String idVue;
private String idRelation;
private RelationType typeRelation;
protected VueRelationUI(Parcel in) {
id = in.readString();
idVue = in.readString();
idRelation = in.readString();
typeRelation = in.readParcelable(RelationType.class.getClassLoader());
}
public static final Creator<VueRelationUI> CREATOR = new Creator<VueRelationUI>() {
#Override
public VueRelationUI createFromParcel(Parcel in) {
return new VueRelationUI(in);
}
#Override
public VueRelationUI[] newArray(int size) {
return new VueRelationUI[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(getId());
parcel.writeString(getIdVue());
parcel.writeString(getIdRelation());
parcel.writeParcelable(getTypeRelation(), flags);
}
}
ENUM
public enum RelationType implements Parcelable {
INNER,
OUTER;
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(ordinal());
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<RelationType> CREATOR = new Creator<RelationType>() {
#Override
public RelationType createFromParcel(Parcel parcel) {
return RelationType.values()[parcel.readInt()];
}
#Override
public RelationType[] newArray(int size) {
return new RelationType[size];
}
};
}
Any help would be much appreciated

The problem happens because of the internal implementation of Android's Parcel class. When you start the new activity, all of the intent extras are parceled and then unparceled. When this happens, the Android framework allocates a new Parcelable[], and not a new ProjetUI[]. So you get a ClassCastException when you try to cast it.
Probably the best solution would be to change your code to use ArrayList<ProjetUI> instead of ProjetUI[]. Then you can use Intent.putParcelableArrayListExtra() and getParcelableArrayListExtra() without any problems.
If you can't do that for some reason, then you will have to manually cast the array one element at a time:
Parcelable[] parcelables = getIntent().getParcelableArrayExtra(ARG_PROJECTS);
ProjetUI[] mProjects = new ProjetUI[parcelables.length];
for (int i = 0; i < parcelables.length; ++i) {
mProjects[i] = (ProjetUI) parcelables[i];
}

Related

Android use Intent to send a list of Parcelable objects which contain a list of another Parcelable objects

In my Android app, I want to use Intent to pass a list of SingleGroup to another Activity where each SingleGroup object contains a list of SingleImage, here are class for SingleImage and SingleGroup:
public class SingleImage implements Parcelable{
public String name;
public int drawableId;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDrawableId() {
return drawableId;
}
public void setDrawableId(int drawableId) {
this.drawableId = drawableId;
}
public SingleImage(String name, int drawableId)
{
this.name = name;
this.drawableId = drawableId;
}
public static final Parcelable.Creator<SingleImage> CREATOR
= new Parcelable.Creator<SingleImage>()
{
public SingleImage createFromParcel(Parcel in)
{
return new SingleImage(in);
}
public SingleImage[] newArray(int size)
{
return new SingleImage[size];
}
};
private SingleImage(Parcel in)
{
name = in.readString();
drawableId = in.readInt();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(drawableId);
}
}
Here is SingleGroup class:
public class SingleGroup implements Parcelable{
private static final String TAG = "SINGLEGROUP";
private ArrayList<SingleImage> images = new ArrayList<SingleImage>(0);
private String myTitle;
private String groupDesc;
public SingleGroup(String mTitle, String desc, ArrayList<SingleImage> imagesList) {
this.myTitle = mTitle;
this.groupDesc = desc;
this.images = imagesList;
}
public String getGroupDesc() {
return groupDesc;
}
public String getMyTitle() {
return myTitle;
}
public void setMyTitle(String myTitle) {
this.myTitle = myTitle;
}
public ArrayList<SingleImage> getImages() {
return images;
}
public void setImages(ArrayList<SingleImage> images) {
this.images = images;
}
public static final Parcelable.Creator<SingleGroup> CREATOR
= new Parcelable.Creator<SingleGroup>()
{
public SingleGroup createFromParcel(Parcel in)
{
return new SingleGroup(in);
}
public SingleGroup[] newArray(int size)
{
return new SingleGroup[size];
}
};
private SingleGroup(Parcel in)
{
myTitle = in.readString();
groupDesc = in.readString();
//problem maybe here
in.readTypedList(images,SingleImage.CREATOR);
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(myTitle);
dest.writeString(groupDesc);
//problem maybe here
dest.writeTypedList(images);
}
#Override
public String toString() {
String result = "myTitle "+myTitle+" desc "+ groupDesc + " imagesize "+images.size();
return result;
}
}
It works ok when calling to pass ArrayList<SingleImage> through Intent,
while when I called to pass ArrayLlist<SingleGroup> through Intent I got error:
Parcel android.os.Parcel#3c4c6743: Unmarshalling unknown type code
3211379 at offset 224
Any idea? Thanks in advance
---------------- Update ----------------
I have changed in.readTypedList(images, null); to in.readTypedList(images, SingleImage.CREATOR); but it still not working. same unmarshalling error
---------------- Problem Solved --------------
Thanks to Keita. The real problem is not actually listed in my code. When I passing the parcelable object, I should also include some other attributes which I missed.
Strongly recommend AS plugin Android Parcelable code generator
You must be pass SingleImage.CREATOR into method readTypedList instead of "null" value
private SingleGroup(Parcel in)
{
myTitle = in.readString();
groupDesc = in.readString();
//problem maybe here
in.readTypedList(images, SingleImage.CREATOR);
}
Try this one. That is what i always do and it work:
private SingleGroup(Parcel in)
{
myTitle = in.readString();
groupDesc = in.readString();
//problem maybe here
in.createTypedArrayList(SingleImage.CREATOR);
}
ArrayList is serialize. So try to this
public class SingleImage implements Serializable{
....
}
Refer this: ArrayList Android Documentation

readParcelable returns null

I try to pass a News class to an activity, so I implemented the Parcelable interface. Inside News I have two classes implementing Parcelable too, Image and Date
The matter is that my News object at the end contains null for fields image and date.
Here my code:
News.java
public class News implements Parcelable {
public static final String TAG = "model_news";
private JSONObject object;
private int id;
private String type;
private String title;
private Boolean comment_disabled;
private String category_name;
private String url;
private Image images;
private Date date;
private Boolean is_video;
public News(JSONObject object) {
this.object = object;
try {
id = Integer.parseInt(object.getString("id"));
type = object.getString("type");
title = object.getString("title");
comment_disabled = object.getBoolean("comment_disabled");
category_name = object.getString("category_name");
url = object.getString("url");
if (!object.isNull("images")) {
images = new Image(object.getJSONObject("images"));
}
date = new Date(object.getJSONObject("date"));
is_video = object.getBoolean("is_video");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
}
}
protected News(Parcel in) {
id = in.readInt();
type = in.readString();
title = in.readString();
category_name = in.readString();
url = in.readString();
images = (Image) in.readParcelable(Image.class.getClassLoader());
date = (Date) in.readParcelable(Date.class.getClassLoader());
is_video = in.readByte() != 0;
comment_disabled = in.readByte() != 0;
}
public static final Creator<News> CREATOR = new Creator<News>() {
#Override
public News createFromParcel(Parcel in) {
return new News(in);
}
#Override
public News[] newArray(int size) {
return new News[size];
}
};
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(type);
dest.writeString(title);
dest.writeByte((byte) (comment_disabled ? 1 : 0));
dest.writeString(category_name);
dest.writeString(url);
dest.writeParcelable(images, flags);
dest.writeParcelable(date, flags);
dest.writeByte((byte) (is_video ? 1 : 0));
}
#Override
public int describeContents() {
return 0;
}
}
Image.java
public class Image implements Parcelable {
public static final String TAG = "model_image";
private JSONObject imageObj;
private JSONObject original;
private String source;
private int width;
private Drawable image;
public Image(JSONObject imageObj) {
this.imageObj = imageObj;
try {
original = this.imageObj.getJSONObject("original");
source = original.getString("src");
width = original.getInt("width");
} catch (JSONException e) {
e.getMessage();
}
}
protected Image(Parcel in) {
source = in.readString();
width = in.readInt();
}
public static final Creator<Image> CREATOR = new Creator<Image>() {
#Override
public Image createFromParcel(Parcel in) {
return new Image(in);
}
#Override
public Image[] newArray(int size) {
return new Image[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(source);
dest.writeInt(width);
}
}
what I'm doing wrong ?
There is mistake in the Parcelable implementation.
First of all parcelable implementation states that: the fields passed in News(Parcel in) Constructor should be written in the same sequence in writeToParcel() method. Thats called Marshalling and Unmarshalling.
Corrections:
Drawable cannot be passed a parameter in Parcelable.
News Parcelable implementation.
Missed some of the fields its just for your understanding.
public class News implements Parcelable {
public static final String TAG = "model_news";
private JSONObject object;
private int id;
private String type;
private String title;
private Boolean comment_disabled;
private String category_name;
private String url;
private Image images;
private Date date;
private Boolean is_video;
protected News(Parcel in) {
id = in.readInt();
type = in.readString();
title = in.readString();
category_name = in.readString();
url = in.readString();
}
public static final Creator<News> CREATOR = new Creator<News>() {
#Override
public News createFromParcel(Parcel in) {
return new News(in);
}
#Override
public News[] newArray(int size) {
return new News[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(type);
dest.writeString(title);
dest.writeString(category_name);
dest.writeString(url);
}
}
public class Image implements Parcelable {
public static final String TAG = "model_image";
private JSONObject imageObj;
private JSONObject original;
private String source;
private int width;
private Drawable image;
protected Image(Parcel in) {
source = in.readString();
width = in.readInt();
}
public static final Creator<Image> CREATOR = new Creator<Image>() {
#Override
public Image createFromParcel(Parcel in) {
return new Image(in);
}
#Override
public Image[] newArray(int size) {
return new Image[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(source);
dest.writeInt(width);
}
}

Starting new activity gives: Class not found when unmarshalling

I'm facing a problem when passing a list of parcelable objects to the intent of one new activity.
Here's what I do:
Intent intent = new Intent(context, CurriculumCorsesActivity.class);
intent.setExtrasClassLoader(CurriculumCourseCard.class.getClassLoader());
intent.putParcelableArrayListExtra("courses", card.getCourses());
context.startActivity(intent);
And when I call the startActivity I get the following error:
E/Parcelīš• Class not found when unmarshalling: pt.tecnico.fenixmobile.person.CurriculumCourseCard, e: java.lang.ClassNotFoundException: pt.tecnico.fenixmobile.person.CurriculumCourseCard
CurriculumCourseCard implements the Parcelable interface. Here's is the implementation:
public class CurriculumCourseCard implements Serializable, Parcelable{
private static final long serialVersionUID = 765578940083L;
private String courseName;
private String finalGrade;
private String ects;
public CurriculumCourseCard(Parcel in) {
this.courseName = in.readString();
this.finalGrade = in.readString();
this.ects = in.readString();
}
public String getCourseName() {
return courseName;
}
public String getFinalGrade() {
return finalGrade;
}
public String getEcts() {
return ects;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(this.courseName);
parcel.writeString(this.finalGrade);
parcel.writeString(this.ects);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public CurriculumCourseCard createFromParcel(Parcel in) {
return new CurriculumCourseCard(in);
}
public CurriculumCourseCard[] newArray(int size) {
return new CurriculumCourseCard[size];
}
};}
And this is how I'm getting the list in the new activity:
Intent intent = getIntent();
intent.setExtrasClassLoader(CurriculumCourseCard.class.getClassLoader());
ArrayList<CurriculumCourseCard> courses = intent.getParcelableArrayListExtra("courses");
In the new activity I see as much items as there are in the list, but no content at all. And the only error is the one described before.
Hope you can help me.

can't pass Parcelable data between activities

I looked everywhere and I think I'm missing something... I trying to pass my ArrayList with my object from activity to activity and getting "Unable to start activity... null pointer exception expected receiver of type EcgResultsHolder"...and the app crashes.
the type:
public class EcgDetails
{
private final String m_EcgProperties;
private final String m_Category;
public EcgDetails(String i_EcgProps, String i_Category)
{
m_EcgProperties = i_EcgProps;
m_Category = i_Category;
}
public String getEcgParams()
{
return m_EcgProperties;
}
public String getCategory()
{
return m_Category;
}
}
public class EcgResultsHolder extends ArrayList<EcgDetails> implements Parcelable
{
private static final long serialVersionUID = 663585476779879096L;
public EcgResultsHolder( )
{
}
public EcgResultsHolder(Parcel in){
readFromParcel(in);
}
#SuppressWarnings("unchecked")
public final Parcelable.Creator CREATOR = new Parcelable.Creator() {
#Override
public EcgResultsHolder createFromParcel(Parcel in) {
return new EcgResultsHolder(in);
}
#Override
public Object[] newArray(int arg0) {
return null;
}
};
private void readFromParcel(Parcel in) {
this.clear();
//First we have to read the list size
int size = in.readInt();
//Reading remember that we wrote first the Name and later the Phone Number.
//Order is fundamental
for (int i = 0; i < size; i++)
{
EcgDetails c = new EcgDetails(in.readString(), in.readString());
this.add(c);
}
}
#Override
public void writeToParcel(Parcel dest, int flags)
{
int size = this.size();
//We have to write the list size, we need him recreating the list
dest.writeInt(size);
//We decided arbitrarily to write first the Name and later the Phone Number.
for (int i = 0; i < size; i++) {
EcgDetails c = this.get(i);
dest.writeString(c.getEcgParams());
dest.writeString(c.getCategory());
}
}
#Override
public int describeContents()
{
return 0;
}
}
this is how I put it:
Bundle bundle = new Bundle();
bundle.putParcelable("EcgList", m_EcgListFromServer);
intent.putExtras(bundle);
this is how I read it on the called activity:
EcgResultsHolder m_EcgList = getIntent().getExtras().getParcelable("EcgList");
Add static qualifier to your CREATOR field. It must be static.
public static final Parcelable.Creator<EcgResultsHolder> CREATOR
= new Parcelable.Creator<EcgResultsHolder>() {
...
}

To pass a parcelable arraylist of objects

I trying to pass array of objects via Parcelable. But after the transfer, the data is converted into something strange
The transmitting part:
for (int i=0; i<res.size(); i++) {
Log.d(LOG_TAG, "id = "+res.get(i).id+" weigth = "+res.get(i).weight);
}
ParcelableProducts Checked = new ParcelableProducts();
Checked.setList(res);
intent.putExtra(ParcelableProducts.class.getCanonicalName(), Checked);
The receiving part:
ParcelableProducts res = (ParcelableProducts) data.getParcelableExtra(ParcelableProducts.class.getCanonicalName());
ArrayList<prProduct> prod = res.prod;
for (int i=0; i<prod.size(); i++) {
Log.d(LOG_TAG, "id = "+prod.get(i).id+" weigth = "+prod.get(i).weight);
}
Classes
Parcelable with ArrayList:
public class ParcelableProducts implements Parcelable {
final private static String LOG_TAG = "ParcelableProducts";
public ArrayList<prProduct> prod;
public ParcelableProducts() {
prod = new ArrayList<prProduct>();
}
public void setList(ArrayList<prProduct> _prod){
prod = _prod;
}
public ArrayList<prProduct> getList() {
return prod;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeList(prod);
}
public static final Parcelable.Creator<ParcelableProducts> CREATOR = new Parcelable.Creator<ParcelableProducts>() {
public ParcelableProducts createFromParcel(Parcel in) {
return new ParcelableProducts(in);
}
public ParcelableProducts[] newArray(int size) {
return new ParcelableProducts[size];
}
};
private ParcelableProducts(Parcel parcel) {
prod = new ArrayList<prProduct>();
parcel.readTypedList(prod, prProduct.CREATOR);
}
}
and prProduct:
public class prProduct implements Parcelable {
final static String LOG_TAG = "prProduct";
public float weight;
public int id;
public prProduct(int _id, Float _weight) {
weight = _weight;
id = _id;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeFloat(weight);
parcel.writeInt(id);
}
public static final Parcelable.Creator<prProduct> CREATOR = new Parcelable.Creator<prProduct>() {
public prProduct createFromParcel(Parcel in) {
return new prProduct(in);
}
public prProduct[] newArray(int size) {
return new prProduct[size];
}
};
private prProduct(Parcel parcel) {
weight = parcel.readFloat();
id = parcel.readInt();
}
}
in log:
Transmit: id = 7 weigth = 0.0
Recive: id = 7602278 weigth = 4.2E-44
Off hand I don't see where the data is getting corrupted in transmission, but it will help to clean up your code. Personally I have fixed many weird bugs in the past when I refactor my code to improve readability. First thing you should do is remove the class "ParcelableProducts" because you don't need it. Just pass the ArrayList in the intent directly using the putParcelableArrayListExtra method. Shown here.
Also this is a bit nit picky but you shouldn't directly access your fields. It is better to set them as private and use getters/setters. Also using a for each loop for your logging statement would be a bit cleaner.
A safer PrProduct class.
//Java classes should start with capital letter
public class PrProduct implements Parcelable {
private final static String LOG_TAG = "PrProduct";
private float weight;
private int id;
public prProduct(int id, float weight) {
this.weight = weight;
this.id = id;
}
//Using getters only makes this immutable which is safer since the
//weight/id aren't likely to change.
public float getWeight(){
return weight;}
public int getId(){
return id;}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeFloat(weight);
parcel.writeInt(id);
}
public static final Parcelable.Creator<prProduct> CREATOR = new
Parcelable.Creator<prProduct>() {
public prProduct createFromParcel(Parcel in) {
return new prProduct(in);
}
public prProduct[] newArray(int size) {
return new prProduct[size];
}
};
private prProduct(Parcel parcel) {
weight = parcel.readFloat();
id = parcel.readInt();
}
}
//A sample for each loop
for(PrProduct product: prod)
Log.d(LOG_TAG, "weight=" + product.getWeight() + " id=" + product.getId());
Why do you need to create the object ParcelableProducts that implements Parcelable? I think you can just pass the arraylist directly using putParcelableArrayListExtra method from the intent?

Categories

Resources