I have created the following parcelable object:
public class ViewModel implements Parcelable {
private String image, price, credit, title, description, id;
public ViewModel(String image, String price, String credit, String title, String description, String id) {
this.image = image;
this.price = price;
this.credit = credit;
this.title = title;
this.description = description;
this.id = id;
}
public String getPrice() {
return price;
}
public String getCredit() {
return credit;
}
public String getDescription() {
return description;
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public String getImage() {
return image;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(new String[] {
this.image,
this.price,
this.credit,
this.title,
this.description,
this.id
});
}
/** Static field used to regenerate object, individually or as arrays */
public static final Parcelable.Creator<ViewModel> CREATOR = new Parcelable.Creator<ViewModel>() {
public ViewModel createFromParcel(Parcel pc) {
return new ViewModel(pc);
}
public ViewModel[] newArray(int size) {
return new ViewModel[size];
}
};
/**Creator from Parcel, reads back fields IN THE ORDER they were written */
public ViewModel(Parcel pc){
image = pc.readString();
price = pc.readString();
credit = pc.readString();
title = pc.readString();
description = pc.readString();
id = pc.readString();
}
}
Now I am sending an ArrayList of ViewModel through bundle:
bundle.putParcelableArrayList("products", viewModels);
Is there anything wrong I am doing? Because I get null Bundle Arguments, but if I send a simple string, everything works.
make this change:
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.image);
dest.writeString(this.price);
dest.writeString(this.credit);
dest.writeString(this.title);
dest.writeString(this.description);
dest.writeString(this.id);
}
getIntent().getParcelableArrayListExtra("product"); // get the list
You are trying to retrieve a parcelable ArrayList. You need to retrive a parcelable ViewModel object that you created. Change:
bundle.putParcelableArrayList("product", viewModels);
and
getIntent().getParcelableArrayList("product");
to:
bundle.putParcelable("product", viewModels);
and
getIntent().getParcelable("product");
Related
In My application I get data from a web service and display those data in recycler view. After that I'm planing to add those data in to local sqlite database and display those data when user open application without internet connection.
Here's a simple model class I'm using to pars JSON result using GSON
public class Repo implements Parcelable {
#SerializedName("id")
#Expose
private Integer id;
#SerializedName("name")
#Expose
private String name;
#SerializedName("url")
#Expose
private String url;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeValue(this.id);
dest.writeString(this.name);
dest.writeString(this.url);
}
public Repo() {
}
protected Repo(Parcel in) {
this.id = (Integer) in.readValue(Integer.class.getClassLoader());
this.name = in.readString();
this.url = in.readString();
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public String getUrl() {
return url;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setUrl(String url) {
this.url = url;
}
public static final Creator<Repo> CREATOR = new Creator<Repo>() {
#Override
public Repo createFromParcel(Parcel source) {
return new Repo(source);
}
#Override
public Repo[] newArray(int size) {
return new Repo[size];
}
};
}
I can create a almost identical model class to represent SQLite data. In here I'm using ORMlite. But this is very similar situation for other ORMs.
#DatabaseTable(tableName = Repo.TABLE_NAME)
public class Repo {
public static final String TABLE_NAME = "repo";
#DatabaseField(columnName = "repo_id")
private long repoId;
#DatabaseField(columnName = "name")
private String name;
public long getRepoId() {
return repoId;
}
public String getName() {
return name;
}
public void setRepoId(long repoId) {
this.repoId = repoId;
}
public void setName(String name) {
this.name = name;
}
}
But by the time I'm trying to save these data in to SQLite database I already have data objects set in GSON model classes. It's kind a redundant thing copy object from GSON model and setting that values in to SQLite model. So I came up with below solution by trying to use single model class to represent both.
#DatabaseTable(tableName = Repo.TABLE_NAME)
public class Repo implements Parcelable {
public static final String TABLE_NAME = "repo";
#DatabaseField(columnName = "repo_id")
#SerializedName("id")
#Expose
private Integer id;
#DatabaseField(columnName = "name")
#SerializedName("name")
#Expose
private String name;
#SerializedName("url")
#Expose
private String url;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeValue(this.id);
dest.writeString(this.name);
dest.writeString(this.url);
}
public Repo() {
}
protected Repo(Parcel in) {
this.id = (Integer) in.readValue(Integer.class.getClassLoader());
this.name = in.readString();
this.url = in.readString();
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public String getUrl() {
return url;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setUrl(String url) {
this.url = url;
}
public static final Creator<Repo> CREATOR = new Creator<Repo>() {
#Override
public Repo createFromParcel(Parcel source) {
return new Repo(source);
}
#Override
public Repo[] newArray(int size) {
return new Repo[size];
}
};
}
I have try this with different type of model class where it only had String type fields. Since GSON uses types like Integer,Boolean That stopping me from using same model for SQLite because database does not identify Integer as a type, in order to work it need to be int.
So what is the professional way to handle this ? Don't I have any other option other than going back to the method of creating two separate model class to represent SQLite and GSON.
Yout approach is absolutely correct, but i think you are putting too much effort reinventing the wheel
You can easily achieve the described task using Room
I am currently trying to pass some data from my fragment to an activity. I am passing some parameter, and also an arraylist of a model class. I already make sure that the arraylist before I passed it is not null, since I am able to print out the value in log. But it become null in the called activity.
Here is the model class:
public class Product implements Parcelable {
String code, id, status, is_expired;
public Product() {}
public Product(String code, String id, String status, String is_expired) {
this.code = code;
this.id = id;
this.status = status;
this.is_expired = is_expired;
}
public Product(Parcel in) {
code = in.readString();
id = in.readString();
status = in.readString();
is_expired = in.readString();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getIs_expired() {
return is_expired;
}
public void setIs_expired(String is_expired) {
this.is_expired = is_expired;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(code);
dest.writeString(id);
dest.writeString(status);
dest.writeString(is_expired);
}
public static final Parcelable.Creator<Product> CREATOR = new Parcelable.Creator<Product>() {
public Product createFromParcel(Parcel in) {
return new Product(in);
}
public Product[] newArray(int size) {
return new Product[size];
}
};
}
Here is the caller:
intent.putExtra("product", productList);
Here is how retrieve the data:
ArrayList<Product> productList = getIntent().getParcelableExtra("product");
What am I doing wrong here?
Try this .
intent.putParcelableArrayListExtra("product", productList);
And next Activity .
ArrayList<Product> productList = getIntent().getParcelableArrayListExtra("product");
In my Android application I am inserting a VideoEntity objects in firebase database this way:
#Override
protected void onCreate(Bundle savedInstanceState){
Log.d(TAG, " onCreate(Bundle) - Ini ");
super.onCreate(savedInstanceState);
database = FirebaseDatabase.getInstance();
String videoId = "";
for(VideoEntity video: videosList) {
videoId = video.getId();
DatabaseReference mRef = database.getReference().child("Videos").push();
mRef.setValue(video);
}
this the VideoEntity class:
package com.app.entities;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable;
public class VideoEntity implements Parcelable{
private String id;
private String title;
private String description;
private String thumbnailURL;
private String category;
public VideoEntity() {
}
public VideoEntity(Parcel in) {
id = in.readString();
title = in.readString();
description = in.readString();
thumbnailURL = in.readString();
category = in.readString();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getThumbnailURL() {
return thumbnailURL;
}
public void setThumbnailURL(String thumbnail) {
this.thumbnailURL = thumbnail;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getCategory() {return category;}
public void setCategory(String category) { this.category = category;}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(title);
dest.writeString(description);
dest.writeString(thumbnailURL);
dest.writeString(category);
}
public static final Creator<VideoEntity> CREATOR = new Creator<VideoEntity>() {
#Override
public VideoEntity createFromParcel(Parcel in) {
return new VideoEntity(in);
}
#Override
public VideoEntity[] newArray(int size) {
return new VideoEntity[size];
}
};
}
The problem is that everytime I start this activity the same objects are inserted in the database, so i have duplicate data in the database.
Is there anyway to avoid this ?
If your data has a natural key, store it under its natural key. In your case the videos have an id field, which likely is unique already. So instead of storing the videos under a push ID, store them under their existing ID:
DatabaseReference mRef = database.getReference().child("Videos").child(video.getId());
mRef.setValue(video);
That way the next time you run the app, it will just write the same data in the same location again.
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
I have an android project where I have a class. In that class is an ArrayList<Choices>. I will be getting some XML, parsing it out, then making objects out of it which I will be passing to another activity. I'm choosing Parcelable for this.
Is Parcelable a good choice? Am I doing everything correctly? I'm not familiar really with Parcelable. My ArrayList is of another class that I made within this class. Will it properly pass that ArrayList of objects to the Parcel with it not extending Parcelable and stuff?
import java.util.ArrayList;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.os.ParcelableCompat;
public class Question implements Parcelable{
String id;
String text;
String image;
ArrayList<Choices> CHOICES;
public Question(String id, String text, String image) {
super();
this.id = id;
this.text = text;
this.image = image;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
#Override
public String toString() {
return "Question [id=" + id + ", text=" + text + ", image=" + image
+ "]";
}
// Answer Choices class
class Choices {
boolean isCorrect;
String choice;
public Choices(boolean isCorrect, String choice) {
this.isCorrect = isCorrect;
this.choice = choice;
}
public String getChoice() {
return choice;
}
public boolean getIsCorrect() {
return isCorrect;
}
#Override
public String toString() {
return "Choices [isCorrect=" + isCorrect + ", choice=" + choice
+ "]";
}
}
public static final Parcelable.Creator<Question> CREATOR = new Parcelable.Creator<Question>() {
#Override
public Question createFromParcel(Parcel in) {
return new Question(in);
}
#Override
public Question[] newArray(int size) {
return new Question[size];
}
};
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(text);
dest.writeString(image);
dest.writeList(CHOICES);
}
private Question(Parcel in) {
this.id = in.readString();
this.text = in.readString();
this.image = in.readString();
this.CHOICES = in.readArrayList(Choices.class.getClassLoader());
}
}
Thanks for any help!
If you need to pass an ArrayList between activities, then I'd go with implementing Parcelable also, as there is no other way around I guess.
However I don't think you will need that much of getters and setters. Here is your Question class which implements Parcelable:
public class Question implements Parcelable {
public String id;
public String text;
public String image;
public ArrayList<Choice> choices;
/**
* Constructs a Question from values
*/
public Question (String id, String text, String image, ArrayList<Choice> choices) {
this.id = id;
this.text = text;
this.image = image;
this.choices = choices;
}
/**
* Constructs a Question from a Parcel
* #param parcel Source Parcel
*/
public Question (Parcel parcel) {
this.id = parcel.readString();
this.text = parcel.readString();
this.image = parcel.readString();
this.choices = parcel.readArrayList(null);
}
#Override
public int describeContents() {
return 0;
}
// Required method to write to Parcel
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(text);
dest.writeString(image);
dest.writeList(choices);
}
// Method to recreate a Question from a Parcel
public static Creator<Question> CREATOR = new Creator<Question>() {
#Override
public Question createFromParcel(Parcel source) {
return new Question(source);
}
#Override
public Question[] newArray(int size) {
return new Question[size];
}
};
}
Use:
in.createTypedArrayList(Product.CREATOR)
In the constructor that takes a Parable object as a param.
In the writeToParcel method use dest.writeTypedList(product);
You have it almost, but not quite, right. The Question class looks nearly correctly Parcelable. The only thing that won't work is parcelling the array of Choices.
There are two ways that you could do it:
Make Choices Parcelable. You will have to add all the required methods, and the CREATOR. Because Android knows how to parcel ArrayLists of Parcelables, this will work.
Make parceling the Array of Choices part of parcelling the Question. To do this, you'd probably push the size of the array into the Parcel, and then loop over the Choices, pushing their values. On the other end, you'd read the count first, and then read the values for each Choice, creating each and pushing it into the new Question.
Create a new java file for "Choices" and implement "Parcelable". If you do not implement parcelable you will get run-time exception (Unable to Marshal). So use the code below :
public class Choices implements Parcelable{
boolean isCorrect;
String choice;
public Choices(boolean isCorrect, String choice) {
this.isCorrect = isCorrect;
this.choice = choice;
}
//Create getters and setters
protected Choices(Parcel in) {
isCorrect = in.readByte() != 0;
choice = in.readString();
}
public static final Creator<Choices> CREATOR = new Creator<Choices>() {
#Override
public Choices createFromParcel(Parcel in) {
return new Choices(in);
}
#Override
public Choices[] newArray(int size) {
return new Choices[size];
}
};
#Override
public String toString() {
return "Choices [isCorrect=" + isCorrect + ", choice=" + choice
+ "]";
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (isCorrect ? 1 : 0));
dest.writeString(choice);
}
}
As mentioned in above answer by #G.Blake you need to make Choices Parcelable and Android knows how to parcel ArrayLists of Parcelables