I have a database in realm and need to pass a realm object as a java object I have created in order for my adapter to recognise it.
public final static Parcelable.Creator<MoviePOJO> CREATOR = new Creator<MoviePOJO>() {
#SuppressWarnings({
"unchecked"
})
public MoviePOJO createFromParcel(Parcel in) {
MoviePOJO instance = new MoviePOJO();
instance.posterPath = ((String) in.readValue((String.class.getClassLoader())));
instance.overview = ((String) in.readValue((String.class.getClassLoader())));
instance.releaseDate = ((String) in.readValue((String.class.getClassLoader())));
instance.id = ((Integer) in.readValue((Integer.class.getClassLoader())));
instance.originalTitle = ((String) in.readValue((String.class.getClassLoader())));
instance.backdropPath = ((String) in.readValue((String.class.getClassLoader())));
instance.voteAverage = ((Double) in.readValue((Double.class.getClassLoader())));
return instance;
}
public MoviePOJO[] newArray(int size) {
return (new MoviePOJO[size]);
}
};
is the movie creator and my RealmObject is:
public class FavoritesItem extends RealmObject implements Parcelable {
#PrimaryKey private Integer movieId;
private String moviePoster;
private String movieBackdrop;
private String movieTitle;
private String movieOverview;
private Double movieRating;
private String movieReleaseDate;
}
How do I get one to the other?
Presuming that, as #cricket_007 suggests, you want to get an object from a Realm DB and then send it somewhere on an Intent (or vice-versa), you have a couple options.
By far the best would be to avoid sending the whole object. If you are just sending it, say, to an Activity or an IntentService, within your own app, just send the primary key (probably movieId). The recipient can open a Realm instance and select the same object. This will be far faster than even parcelling.
If you absolutely must parcel, you are going to want an adapter, like this:
public class MovieParcelable implements Parcelable {
public static final Creator<MovieParcelable> CREATOR = new Creator<MovieParcelable>() {
#Override
public MovieParcelable createFromParcel(Parcel in) {
int id = in.readInt();
String poster = in.readString();
// ...
double rating = in.readDouble();
String releaseDate = in.readString();
return new MovieParcelable(id, poster, ..., rating, releaseDate);
}
#Override
public MovieParcelable[] newArray(int size) {
return new MovieParcelable[size];
}
};
private final Movie movie;
public MovieParcelable(Movie movie) {
this.movie = movie;
}
MovieParcelable(int id, String poster, ..., double rating, String releaseDate) {
this(new Movie(id, poster, ..., rating, releaseDate));
}
public Movie getMovie() { return movie; }
#Override
public int describeContents() { return 0; }
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(movie.getId());
dest.writeString(movie.getPoster());
// ...
dest.writeDouble(movie.getRating());
dest.writeString(movie.getReleaseDate());
}
}
Related
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.
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.
The String variable finalMapSearchUrl gets concatenated with another String (mapUrlParam) in the constructor and the Log shows the expected value : Final map URL : https://www.google.com/maps/search/?api=1&query=Spilia%20Beach (here the mapUrlParam = Spilia Beach). However, when i call the getMapSearchUrl() method from outside the class and monitor the Log, the finalMapSearchUrl's value is now back to the default https://www.google.com/maps/search/?api=1&query=. Log in getMapSearchUrl() : finalMapSearchUrl = https://www.google.com/maps/search/?api=1&query=. Any ideas on when,why and how it's value is not preserved outside of the constructor?
PlaceObject.java class:
public class PlaceObject implements Parcelable { // Implementing the Parcelable interface to allow for cleaner and faster code
private static final String TAG = PlaceObject.class.getSimpleName();
private static final String baseMapSearchUrl = "https://www.google.com/maps/search/?api=1&query="; // Base url for launching a Map activity with a Search Intent
// Using int so that the values can be accessed via R.string etc.
private int name;
private int description;
private int category;
private String locationDistance;
private String finalMapSearchUrl = baseMapSearchUrl;
PlaceObject(int name, int description, int category , String locationDistance, String mapUrlParam) {
this.name = name;
this.description = description;
this.locationDistance = locationDistance;
this.category = category;
finalMapSearchUrl += Uri.encode(mapUrlParam);
Log.d(TAG,"Final map URL : " + finalMapSearchUrl);
}
private PlaceObject(Parcel in) {
name = in.readInt();
description = in.readInt();
locationDistance = in.readString();
category = in.readInt();
}
public static final Creator<PlaceObject> CREATOR = new Creator<PlaceObject>() {
#Override
public PlaceObject createFromParcel(Parcel in) {
return new PlaceObject(in);
}
#Override
public PlaceObject[] newArray(int size) {
return new PlaceObject[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(name);
parcel.writeInt(description);
parcel.writeString(locationDistance);
parcel.writeInt(category);
}
public int getName() {
return name;
}
public int getDescription() {
return description;
}
public String getLocationDistance() {
return locationDistance;
}
public int getCategory() {
return category;
}
public String getMapSearchUrl() {
Log.d(TAG,"finalMapSearchUrl = " + finalMapSearchUrl);
return finalMapSearchUrl; //TODO:sp figure out why the variable's value gets lost after the constructor is done
}
}
Because you're simply getting the base url and not the one Parceled.
Solution:
Add it to parcel and pay attention to the order of writing,
Like this:
private PlaceObject(Parcel in) {
name = in.readInt();
description = in.readInt();
category = in.readInt();
locationDistance = in.readString();
finalMapSearchUrl = in.readString();
}
and don't forget to fix this as well:
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(name);
parcel.writeInt(description);
parcel.writeInt(category);
parcel.writeString(locationDistance);
parcel.writeString(finalMapSearchUrl);
}
I'm trying to save my data using SharedPreferences by converting my ArrayList into JSON. Whenever I try to retrieve data and convert it back I get cannot cast ArrayList into Playlist error. Any ideas what's might be wrong here?
My Playlist class:
public class Playlist extends ArrayList<Parcelable> implements Parcelable{
private ArrayList<Song> playlistSongs;
private String name;
public Playlist(String name) {
this.name = name;
this.playlistSongs = new ArrayList<>();
}
protected Playlist(Parcel in) {
playlistSongs = in.createTypedArrayList(Song.CREATOR);
name = in.readString();
}
public static final Creator<Playlist> CREATOR = new Creator<Playlist>() {
#Override
public Playlist createFromParcel(Parcel in) {
return new Playlist(in);
}
#Override
public Playlist[] newArray(int size) {
return new Playlist[size];
}
};
public ArrayList<Song> getPlaylistSongs() {
return playlistSongs;
}
public String getName() {
return name;
}
#Override
public String toString() {
return name;
}
public int getCount(){
return playlistSongs.size();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeTypedList(playlistSongs);
parcel.writeString(name);
}
#Override
public Stream<Parcelable> stream() {
return null;
}
#Override
public Stream<Parcelable> parallelStream() {
return null;
}
}
My convert to JSON method I call after creating all of the playlists in activity:
public boolean writeJSON(ArrayList<Playlist> playlists) {
SharedPreferences mSettings = getSharedPreferences("Playlists", Context.MODE_PRIVATE);
SharedPreferences.Editor mEditor = mSettings.edit();
Type type = new TypeToken<ArrayList<Playlist>>() {
}.getType();
try {
String writeValue = mGson.toJson(playlists, type);
mEditor.putString("Playlists", writeValue);
mEditor.commit();
return true;
} catch (Exception e) {
return false;
}
}
And my read JSON method I call after user relaunch application:
public ArrayList<Playlist> readJSON() {
SharedPreferences mSettings = getSharedPreferences("Playlists", Context.MODE_PRIVATE);
Type collectionType = new TypeToken<ArrayList<Playlist>>() {
}.getType();
String loadValue = mSettings.getString("Playlists", null);
ArrayList<Playlist> temp = mGson.fromJson(loadValue, collectionType);
return temp;
}
I believe the issue is that the Playlist class extends ArrayList. GSON is trying to serialize your Playlist as a list of Parcelable. Judging by your class, it doesn't look like Playlist truly needs to extend ArrayList. I tried running your example (I had to remove a lot in order to get down to the bare minimum) and it was serializing each Playlist as an empty list (the string "[]"), regardless of what was in playlistSongs. I removed the superclass and it started working as expected.
I think that the problem is this extends ArrayList<Parcelable>. Try to make it extends ArrayList<Song> and also you do not need to hold a reference for the Arraylist (private ArrayList<Song> playlistSongs;)
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.