6

I want to save a custom object myObject in shared preferences. Where this custom object has ArrayList<anotherCustomObj>. This anotherCustomObj has primary variables.

Both myObject and anotherCustomObj are parcelable.

I tried below code to convert it to String and save it :

String myStr = gson.toJson(myObject);
editor.putString(MY_OBJ, myStr);

But it gives RunTimeException.

EDIT : Below is logcat screen shot. enter image description here

anotherCustomObj implementation :

package com.objectlounge.ridesharebuddy.classes;

import java.io.File;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.util.Log;

import com.google.gson.annotations.SerializedName;
import com.objectlounge.ridesharebuddy.R;

public class RS_SingleMatch implements Parcelable {

    private static final String RIDESHARE_DIRECTORY = "RideShareBuddy";
    private static final String TAG = "RS_SingleMatch";
    private static final String IMAGE_PATH = "imagePath";
    private static final String IMAGE_NAME_PREFIX = "RideShareBuddyUserImage";

    private Context context;
    @SerializedName("id")
    private int userId;
    private int tripId;
    private String imageUrl;
    @SerializedName("userName")
    private String email;
    private String realName;
    private String gender;
    private int reputation;
    private String createdAt;
    private String birthdate;
    private float fromLat, fromLon, toLat, toLon;
    private String fromPOI, toPOI;
    private String departureTime;
    private int matchStrength;

    // Constructor
    public RS_SingleMatch(Context context) {
        this.context = context;
    }

    // Constructor to use when reconstructing an object from a parcel
    public RS_SingleMatch(Parcel in) {
        readFromParcel(in);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    // Called to write all variables to a parcel
    public void writeToParcel(Parcel dest, int flags) {

        dest.writeInt(userId);
        dest.writeInt(tripId);
        dest.writeString(imageUrl);
        dest.writeString(email);
        dest.writeString(realName);
        dest.writeString(gender);
        dest.writeInt(reputation);
        dest.writeString(createdAt);
        dest.writeString(birthdate);
        dest.writeFloat(fromLat);
        dest.writeFloat(fromLon);
        dest.writeFloat(toLat);
        dest.writeFloat(toLon);
        dest.writeString(fromPOI);
        dest.writeString(toPOI);
        dest.writeString(departureTime);
        dest.writeInt(matchStrength);
    }

    // Called from constructor to read object properties from parcel
    private void readFromParcel(Parcel in) {

        // Read all variables from parcel to created object
        userId = in.readInt();
        tripId = in.readInt();
        imageUrl = in.readString();
        email = in.readString();
        realName = in.readString();
        gender = in.readString();
        reputation = in.readInt();
        createdAt = in.readString();
        birthdate = in.readString();
        fromLat = in.readFloat();
        fromLon = in.readFloat();
        toLat = in.readFloat();
        toLon = in.readFloat();
        fromPOI = in.readString();
        toPOI = in.readString();
        departureTime = in.readString();
        matchStrength = in.readInt();
    }

    // This creator is used to create new object or array of objects
    public static final Parcelable.Creator<RS_SingleMatch> CREATOR = new Parcelable.Creator<RS_SingleMatch>() {

        @Override
        public RS_SingleMatch createFromParcel(Parcel in) {
            return new RS_SingleMatch(in);
        }

        @Override
        public RS_SingleMatch[] newArray(int size) {
            return new RS_SingleMatch[size];
        }
    };

    // Getters
    public int getUserId() {
        return userId;
    }

    public int getTripId() {
        return tripId;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public Bitmap getImage() {

        Bitmap image = null;

        // If imageUrl is not empty
        if (getImageUrl().length() > 0) {

            SharedPreferences prefs = PreferenceManager
                    .getDefaultSharedPreferences(this.context);
            String imagePath = prefs.getString(IMAGE_PATH + getUserId(), "");

            // Get image from cache
            if ((image = RS_FileOperationsHelper.getImageAtPath(imagePath)) == null) {

                Log.d(TAG, "Image not found on disk.");
                Thread t = new Thread(new Runnable() {

                    @Override
                    public void run() {
                        // If image not found on storage then download it
                        setImage(downloadImage(getImageUrl()));
                    }
                });
                t.start();
            }

        } else {
            // Use default image
            image = getDefaultProfileImage();
        }

        image = RS_ImageViewHelper.getRoundededImage(image, image.getWidth());
        Log.d(TAG, "Image width : " + image.getWidth());
        return image;
    }

    public String getEmail() {
        return email;
    }

    public String getRealName() {
        return realName;
    }

    public String getGender() {
        return gender;
    }

    public int getReputation() {
        return reputation;
    }

    public String getCreatedAt() {
        return createdAt;
    }

    public String getBirthdate() {
        return birthdate;
    }

    public float getFromLat() {
        return fromLat;
    }

    public float getFromLon() {
        return fromLon;
    }

    public float getToLat() {
        return toLat;
    }

    public float getToLon() {
        return toLon;
    }

    public String getFromPOI() {
        return fromPOI;
    }

    public String getToPOI() {
        return toPOI;
    }

    public String getDepartureTime() {
        return departureTime;
    }

    public int getMatchStrength() {
        return matchStrength;
    }

    // Setters
    public void setContext(Context context) {
        this.context = context;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public void setTripId(int tripId) {
        this.tripId = tripId;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    public void setImage(Bitmap img) {

        if (img != null) {

            // Get cache directory's path and append RIDESHARE_DIRECTORY.
            String cacheDirStoragePath = context.getCacheDir()
                    + "/"
                    + RIDESHARE_DIRECTORY;

            // Create directory at cacheDirStoragePath if does not exist.
            if (RS_FileOperationsHelper
                    .createDirectoryAtPath(cacheDirStoragePath)) {

                String imagePath = cacheDirStoragePath + "/"
                        + IMAGE_NAME_PREFIX + this.userId + ".png";

                // Save new image to cache
                RS_FileOperationsHelper.saveImageAtPath(img, imagePath, this.context);

                SharedPreferences pref = PreferenceManager
                        .getDefaultSharedPreferences(context);
                Editor e = pref.edit();
                e.putString(IMAGE_PATH + getUserId(), imagePath);
                e.commit();
            }
        }
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setRealName(String realName) {
        this.realName = realName;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setReputation(int reputation) {
        this.reputation = reputation;
    }

    public void setCreatedAt(String createdAt) {
        this.createdAt = createdAt;
    }

    public void setBirthdate(String birthdate) {
        this.birthdate = birthdate;
    }

    public void setFromLat(float fromLat) {
        this.fromLat = fromLat;
    }

    public void setFromLon(float fromLon) {
        this.fromLon = fromLon;
    }

    public void setToLat(float toLat) {
        this.toLat = toLat;
    }

    public void setToLon(float toLon) {
        this.toLon = toLon;
    }

    public void setFromPOI(String fromPOI) {
        this.fromPOI = fromPOI;
    }

    public void setToPOI(String toPOI) {
        this.toPOI = toPOI;
    }

    public void setDepartureTime(String departureTime) {
        this.departureTime = departureTime;
    }

    public void setMatchStrength(int matchStrength) {
        this.matchStrength = matchStrength;
    }

    // calculates age using given date
    @SuppressLint("SimpleDateFormat")
    public int calculateAge(String date) {
        int age = 0;
        try {
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            Date bdate = formatter.parse(date);
            Calendar lCal = Calendar.getInstance();
            lCal.setTime(bdate);
            int lYear = lCal.get(Calendar.YEAR);
            int lMonth = lCal.get(Calendar.MONTH) + 1;
            int lDay = lCal.get(Calendar.DATE);

            Calendar dob = Calendar.getInstance();
            Calendar today = Calendar.getInstance();

            dob.set(lYear, lMonth, lDay);

            age = today.get(Calendar.YEAR) - dob.get(Calendar.YEAR);

            if (today.get(Calendar.DAY_OF_YEAR) < dob.get(Calendar.DAY_OF_YEAR)) {
                age--;
            }

        } catch (ParseException e) {
            e.printStackTrace();
        }
        return age;
    }

    // Download image if not available
    protected void downloadAndSaveImage() {

        SharedPreferences prefs = PreferenceManager
                .getDefaultSharedPreferences(this.context);
        String imagePath = prefs.getString(IMAGE_PATH + getUserId(), "");
        File file = new File(imagePath);

        // If image path not stored in user defaults or image does not exists
        // then
        if ((imagePath == null || !file.exists()) && this.imageUrl.length() > 0) {

            // Download on separate thread
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    setImage(downloadImage(getImageUrl()));
                }
            });
            t.start();
        }
    }

    // Download an image
    private Bitmap downloadImage(String imageUrl) {

        Log.d(TAG, "Image url : " + imageUrl);
        Bitmap image = null;

        try {

            // Download an image from url
            InputStream in = new java.net.URL(imageUrl.trim()).openStream();
            image = BitmapFactory.decodeStream(in);

        } catch (Exception e) {
            e.printStackTrace();
        }

        Log.d(TAG, "Image downloading complete. image : " + image);

        return image;
    }

    // Get default image
    protected Bitmap getDefaultProfileImage() {

        Bitmap image = BitmapFactory.decodeResource(
                this.context.getResources(), R.drawable.default_male);

        if (this.gender.toUpperCase(Locale.US).startsWith("F")) {
            image = BitmapFactory.decodeResource(this.context.getResources(),
                    R.drawable.default_female);
        }

        return image;
    }
}
Geek
  • 8,280
  • 17
  • 73
  • 137
  • 2
    put exception log here – Pankaj Kumar Oct 10 '13 at 13:00
  • @PankajKumar Please look at edited question. – Geek Oct 10 '13 at 13:04
  • @PankajKumar I already asked question for that. If you want to look : http://stackoverflow.com/questions/19294555/failure-delivering-result-resultinfo-who-null-request-4001-result-1-data-null – Geek Oct 10 '13 at 13:05
  • The error line you should take more into account is: Caused by: IllegalArgumentException: class BorindLayout declares multiple JSON fields named mPaint. – damian Oct 10 '13 at 13:05
  • @damian I saw it. Could not understand what BorindLayout and mPaint is. Can you explain? – Geek Oct 10 '13 at 13:06
  • 1
    http://stackoverflow.com/questions/15410037/gson-throws-an-exception-on-4-0-devices check this. "For some reason, this library can't serialize view components any more." – damian Oct 10 '13 at 13:09
  • @damian But my object is not a view component. – Geek Oct 10 '13 at 13:12
  • then paste the structure of your object. – damian Oct 10 '13 at 13:12
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/38974/discussion-between-akash-and-damian) – Geek Oct 10 '13 at 13:13
  • @damian Is there any other way to save custom object? – Geek Oct 10 '13 at 13:52
  • @damian I had one view component. Bitmap. I commented it still getting error. I am posting whole class implementation of custom object. – Geek Oct 11 '13 at 06:31
  • have you seen this http://stackoverflow.com/questions/5418160/store-and-retrieve-a-class-object-in-shared-preference – Developer Oct 11 '13 at 06:40
  • why you want to save custom objects to be shared pref...shared pref is better to save simple data .. this tutorial http://developer.android.com/reference/android/content/SharedPreferences.Editor.html – Developer Oct 11 '13 at 06:40
  • @GauravPandey Because my activity will be destroyed for some reason and recreated. When it is recreated I will not have old variables. That is why I need to save it temporarily. – Geek Oct 11 '13 at 06:53

1 Answers1

3

Link posted by damian was helpful to solve my problem. However, in my case there was no view component in custom object.

According to my observation if you find multiple JSON fields for ANY_VARIABLE_NAME, then it is likely that it is because GSON is not able to convert the object. And you can try below code to solve it.

Add below class to to tell GSON to save and/or retrieve only those variables who have Serialized name declared.

class Exclude implements ExclusionStrategy {

    @Override
    public boolean shouldSkipClass(Class<?> arg0) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean shouldSkipField(FieldAttributes field) {
        SerializedName ns = field.getAnnotation(SerializedName.class);
        if(ns != null)
            return false;
        return true;
    }
}

Below is the class whose object you need to save/retrieve. Add @SerializedName for variables that needs to saved and/or retrieved.

class myClass {
    @SerializedName("id")
    int id;
    @SerializedName("name")
    String name;
}

Code to convert myObject to jsonString :

Exclude ex = new Exclude();
    Gson gson = new GsonBuilder().addDeserializationExclusionStrategy(ex).addSerializationExclusionStrategy(ex).create();
String jsonString = gson.toJson(myObject);

Code to get object from jsonString :

Exclude ex = new Exclude();
Gson gson = new GsonBuilder().addDeserializationExclusionStrategy(ex).addSerializationExclusionStrategy(ex).create();
myClass myObject = gson.fromJson(jsonString, myClass.class);
Geek
  • 8,280
  • 17
  • 73
  • 137