1

I have simple class Track, which stores information about route:

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;

import android.location.Location;

public class Track implements Serializable {
    private static final long serialVersionUID = -5317697499269650204L;

    private Date date;
    private String name;
    private int time;
    private double distance, speed;
    private ArrayList<Location> route;

    public Track(String name, int time, double distance, ArrayList<Location> route) {
        this.date = new Date();
        this.name = name;
        this.time = time;
        this.distance = distance;
        this.speed = distance / (time / 3600.);
        this.route = route;
    }

    public String getDate() {
        return String.format("Date: %1$td-%1$tb-%1$tY%nTime: %1$tH:%1$tM:%1$tS", date);
    }

    public String getName() {
        return name;
    }

    public int getTime() {
        return time;
    }

    public double getDistance() {
        return distance;
    }

    public float getSpeed() {
        return (float) speed;
    }

    public ArrayList<Location> getRoute() {
        return route;
    }

    @Override
    public String toString() {
        return String.format("Name: %s%nDate: %2$td-%2$tb-%2$tY%nTime: %2$tH:%2$tM:%2$tS", name, date);
    }
}

And I'm passing it from one activity to another:

Intent showTrackIntent = new Intent(TabSavedActivity.this, ShowTrackActivity.class);
showTrackIntent.putExtra("track", adapter.getItem(position));
startActivity(showTrackIntent);

Where (Track object is element on ListView). I get error during passing Track object:

java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = classes.Track)

What is happening?

Kamil Lelonek
  • 14,592
  • 14
  • 66
  • 90
  • 1
    you should implement the Parcalabe interface. – Habib Zare Sep 04 '12 at 19:21
  • 1
    The error is because you don't have setters for your private variables, so the serialization process has no way to set those variables. Try writing setters and testing and post any errors. – eternalmatt Sep 04 '12 at 19:27
  • The comment from @eternalmatt makes no sense because you are getting the error while **writing** the object (ie: serializing it) and for that you don't need setters. In any case you don't need to have setter or getter methods for Java serialization to work. – David Wasser Sep 04 '12 at 21:28
  • No setters are needed with serialization. – Kamil Lelonek Sep 05 '12 at 17:13

4 Answers4

1

I think the problem is that Location is not serializable. There are a lot of questions on SE from people who have problems trying to serialize Location objects. However, Location is Parcelable, so maybe you would have better luck just implementing Parcelable for your class instead of using Serializable

David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • I tried to timplement Parcelable interface, but I had a problem with parceling ArrayList. I dont't know what should I use: readList (route, Location.class.getClassLoader()) or readTypedList(route, Location.CREATOR) - both of them didn't work. – Kamil Lelonek Sep 05 '12 at 05:16
  • You should be able to use either `writeTypedList`/`readTypedList` or `writeList`/`readArrayList` or `writeList`/`readList` to do it. What errors did you get? – David Wasser Sep 05 '12 at 08:41
  • So I implemented parcelable and everything goes fine until another activity tries to read from parcel, especially reading ArrayList. The error is: java.lang.RuntimeException: Parcel android.os.Parcel@40609748: Unmarshalling unknown type code 6357098 at offset 160. P.S. How should I format comments as you did? – Kamil Lelonek Sep 05 '12 at 17:02
  • I looked at your answer and made a comment. You wrote the date to the parcel but never read it back. This causes bad stuff to happen. To format your comments you can use the backticks to get "code style" or you can use double asterisks to get bold, etc. click on the blue "help" link to the right of the comment box. – David Wasser Sep 05 '12 at 21:42
0

The current code is:

package classes;

import java.util.ArrayList;
import java.util.Date;

import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;

public class Track implements Parcelable {
    private String name, date;
    private int time;
    private double distance, speed;
    private ArrayList<Location> route;

public Track(String name, int time, double distance, Date date, ArrayList<Location> route) {
    this.name = name;
    this.date = formatDate(date);
    this.time = time;
    this.distance = distance;
    this.route = route;

    this.speed = distance / (time / 3600.);
}

public void writeToParcel(Parcel dest, int flags) {
    dest.writeStringArray(new String[] { name, date });
    dest.writeInt(time);
    dest.writeDouble(distance);
    dest.writeSerializable(date);
    dest.writeTypedList(route);
}

private Track(Parcel in) {
    String[] strings = new String[2];
    in.readStringArray(strings);

    this.name = strings[0];
    this.date = strings[1];
    this.time = in.readInt();
    this.distance = in.readDouble();

    route = new ArrayList<Location>();
    in.readTypedList(route, Location.CREATOR);

    this.speed = distance / (time / 3600.);
}

public static final Parcelable.Creator<Track> CREATOR = new Parcelable.Creator<Track>() {

    public Track createFromParcel(Parcel in) {
        return new Track(in);
    }

    public Track[] newArray(int size) {
        return new Track[size];
    }
};

public String getName() {
    return name;
}

public String getDate() {
    return date;
}

public int getTime() {
    return time;
}

public double getDistance() {
    return distance;
}

public float getSpeed() {
    return (float) speed;
}

public ArrayList<Location> getRoute() {
    return route;
}

public String formatDate(Date date) {
    return String.format("Date: %1$td-%1$tb-%1$tY%nTime: %1$tH:%1$tM:%1$tS", date);
}

@Override
public String toString() {
    return String.format("Name: %s%nDate: %s", name, date);
}

public int describeContents() {
    return 0;
}

}

I don't know why but while I passing my Track through activities I only got one element in route List even that I checked it before new intent is started and there is full list.

Kamil Lelonek
  • 14,592
  • 14
  • 66
  • 90
  • You wrote the date to the parcel (using `dest.writeSerializable(date)`), but you don't read it when unparceling (in the constructor `Track(Parcel in)`. That's why it fails when trying to read it back. When using `Parcelable` you absolutely need to make sure that you read exactly what you write. – David Wasser Sep 05 '12 at 21:40
  • What about the code: `route = new ArrayList(); in.readTypedList(route, Location.CREATOR);` – Kamil Lelonek Sep 06 '12 at 03:20
  • As documentation says: 'Read into the given List items containing a particular object type that were written with writeTypedList(List) at the current dataPosition().' I don't understand what exactly 'at the current dataPosition()' means. Maybe there's a problem. – Kamil Lelonek Sep 06 '12 at 09:13
  • have you removed `dest.writeSerializable(date);` from `writeToParcel()`? If you have made changes to your code, please update the code in the answer. – David Wasser Sep 06 '12 at 11:42
  • "at the current position" refers to how it parcels the data into the `Parcel`. It is correct, don't worry about that. The problem lies elsewhere. – David Wasser Sep 06 '12 at 11:43
  • 1
    No no no. You are still writing the "date" field to the parcel in `dest.writeSerializable(date)` but when you unparcel it you don't read that. You start reading the Location array. That's your problem. Please remove `dest.writeSerializable(date)` from `writeToParcel()`. – David Wasser Sep 06 '12 at 12:43
  • 1
    A `Parcel` is **not** a hash table. It doesn't contain key/value pairs. It is just a stream of bytes. You must ensure that you read exactly the same data types in exactly the same order as when you wrote them. Otherwise you get junk, crashes, or other bad stuff. – David Wasser Sep 06 '12 at 12:46
0

I think that solution is now ready. Here's the code:

import java.util.ArrayList;
import java.util.Date;

import android.location.Location; 
import android.os.Parcel;
import android.os.Parcelable;

public class Track implements Parcelable {
private String name, date;
private int time;
private double distance, speed;
private ArrayList<Location> route;

public Track(String name, int time, double distance, Date date, ArrayList<Location> route) {
    this.name = name;
    this.date = formatDate(date);
    this.time = time;
    this.distance = distance;
    this.route = route;

    this.speed = distance / (time / 3600.);
}

public void writeToParcel(Parcel dest, int flags) {
    dest.writeStringArray(new String[] { name, date });
    dest.writeInt(time);
    dest.writeDouble(distance);
    dest.writeTypedList(route);
}

private Track(Parcel in) {
    String[] strings = new String[2];
    in.readStringArray(strings);
    this.name = strings[0];
    this.date = strings[1];

    this.time = in.readInt();
    this.distance = in.readDouble();

    route = new ArrayList<Location>();
    in.readTypedList(route, Location.CREATOR);

    this.speed = distance / (time / 3600.);
}

public static final Parcelable.Creator<Track> CREATOR = new Parcelable.Creator<Track>() {

    public Track createFromParcel(Parcel in) {
        return new Track(in);
    }

    public Track[] newArray(int size) {
        return new Track[size];
    }
};

public String getName() {
    return name;
}

public String getDate() {
    return date;
}

public int getTime() {
    return time;
}

public double getDistance() {
    return distance;
}

public float getSpeed() {
    return (float) speed;
}

public ArrayList<Location> getRoute() {
    return route;
}

public String formatDate(Date date) {
    return String.format("Date: %1$td-%1$tb-%1$tY%nTime: %1$tH:%1$tM:%1$tS", date);
}

@Override
public String toString() {
    return String.format("Name: %s%nDate: %s", name, date);
}

public int describeContents() {
    return 0;
}
}

David Wasser you are extremely helpful, thanks!

Kamil Lelonek
  • 14,592
  • 14
  • 66
  • 90
0

Serialization:

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(savedTracksFile));
    oos.writeObject(serializedTracks);
    oos.close();

Deserialization:

    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(savedTracksFile));
    serializedTracks = (ArrayList<Track>) ois.readObject();
    ois.close();

Where savedTracksFile is:

    File savedLocationsDirectory = new File(Environment.getExternalStorageDirectory().getPath().concat("/AppFolder"));
    if (!savedLocationsDirectory.exists()) savedLocationsDirectory.mkdirs();
    savedTracksFile = new File(savedLocationsDirectory, "savedTracks.gc");
Kamil Lelonek
  • 14,592
  • 14
  • 66
  • 90
  • I can't serialize my `Track` class object, because `Location` object is not serializable. I changed `ArrayList` of locations to `HashMap` because actually I only need Latitude and Longitude so I think that is the simple way to keep it, move between activities and also serialize into file. – Kamil Lelonek Sep 07 '12 at 14:34
  • But it mixes points in diffrent way there were written into Map. Maybe I should implement my own class pair, and save it into `ArrayList`. – Kamil Lelonek Sep 07 '12 at 15:03