Question

I need to serialize a list of GeoPoints then read them back in. I searched the web and found out that I can implement my own GeoPoint and implement serializable. It works and I can write the object to a .ser file but when i read it back in i get an invalidclassexception, which goes on to say detail message - illegalaccessexception of the original GeoPoint object. I assume I'm not extending GeoPoint in MyGeoPoint class properly. could someone take a look and tell me where I'm making the error?

import com.google.android.maps.GeoPoint;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class MyGeoPoint extends GeoPoint implements Serializable {
static final long serialVersionUID = -3010695769693014199L;


public MyGeoPoint(int latitude, int longitude){
    super(latitude, longitude);
}
public MyGeoPoint(){
    this(0,0);
}
private void writeObject(ObjectOutputStream s) throws IOException{
    s.defaultWriteObject();
}
private void readObject(ObjectInputStream s)
        throws java.io.IOException, java.lang.ClassNotFoundException{
    s.defaultReadObject();
}
}

and then it gets used like

File filex = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator +"floodData.ser");
    MyGeoPoint aPoint = new MyGeoPoint();
    MyGeoPoint readPoint = null;
    //write
    try{
    FileOutputStream f = new FileOutputStream(filex);
    ObjectOutputStream s = new ObjectOutputStream(f);
    s.writeObject(aPoint);
    s.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

    //read
    try {
    FileInputStream ff = new FileInputStream(filex);
    ObjectInputStream ss = new ObjectInputStream(ff);
    readPoint = (MyGeoPoint)ss.readObject();
    ss.close();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } 
    catch (Exception e) {
        e.printStackTrace();
    } 
    //end serialisationtest 
Was it helpful?

Solution

The problem is that GeoPoint does not have a no-arg constructor, see the JavaDoc of Serializable:

To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.

And the JavaDoc of InvalidClassException

Thrown when the Serialization runtime detects one of the following problems with a Class.

  • The serial version of the class does not match that of the class descriptor read from the stream
  • The class contains unknown datatypes
  • The class does not have an accessible no-arg constructor

OTHER TIPS

You do not need to extend com.google.android.maps.GeoPoint to have a serializable object to store your lat/lon coordinates.

And since you do not own the original object, you should not do it because if Google decides to change the original GeoPoint and let's say... add/remove methods, change method signatures, etc... you may get into troubles.

Even worse, your code won't be portable, FYI Android on a Kindle Fire does not have access to the Google Maps API, so your App extending from com.google.android.maps.GeoPoint won't work.

The way to go is to create a Serializable POJO with the same exact properties (and its types) to store your lat/lon coordinates that you'll serialize:

package com.yourapp.beans;

public class YourGeoPoint implements Serializable {

        private static final long serialVersionUID = -99127398709809L;

        private int latitudeE6;
        private int longitudeE6;

        public YourGeoPoint() {
            this.setLatitudeE6(0);
            this.setLongitudeE6(0);
        }

        public YourGeoPoint(int latitudeE6, int longitudeE6) {
            this.setLatitudeE6(latitudeE6);
            this.setLongitudeE6(longitudeE6);
        }

        public int getLatitudeE6() {
            return latitudeE6;
        }

        public void setLatitudeE6(int latitudeE6) {
            this.latitudeE6 = latitudeE6;
        }

        public int getLongitudeE6() {
            return longitudeE6;
        }

        public void setLongitudeE6(int longitudeE6) {
            this.longitudeE6 = longitudeE6;
        }

    }

Then whenever you need an com.google.android.maps.GeoPoint... and after de-serializing to a yourGeoPoint instance, just do:

import com.google.android.maps.GeoPoint;
import com.yourapp.beans.YourGeoPoint;
//...    
YouGeoPoint yourGeoPoint = // De-serialization...        
GeoPoint geoPoint = new GeoPoint(yourGeoPoint.getLatitudeE6(), yourGeoPoint.getLongitudeE6());

It works 100% and your code will be portable to Android Devices where the Google Maps API is not present... if that is the case, just import whatever map libraries you do have access to (such as ESRI's or OpenSignalMaps) and get one of their "GeoPoints" or equivalent object with the coordinates coming from your own de-serialized object.

Cheers!

Just to make this answer more complete, I've attempted to solve the problem with a bandaid fix. Try adding the following lines to the class writeObject and readObject...

private void writeObject(ObjectOutputStream s) throws IOException{
    s.writeInt(this.getLatitudeE6());
    s.writeInt(this.getLongitudeE6());
}
private void readObject(ObjectInputStream s)
        throws java.io.IOException, java.lang.ClassNotFoundException{
    this(s.readInt(), s.readInt());
}

A cleaner approach without messing up other code is to change your class setter and getter to:

private double lat;
private double lng;

public LatLng getLocation() {
    LatLng latLng = new LatLng(this.lat, this.lng);

    return latLng;
}

public void setLocation(LatLng location) {

    this.lat = location.latitude;
    this.lng = location.longitude;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top