简体   繁体   中英

Why serialization of object that implements Serializable throws exception?

I'm trying to pass two ArrayLists via Intent to another activity. Created this class to store these two arrays.

public class StorageBin implements Serializable {
    //storage variables
    public ArrayList<String> placesList;
    public ArrayList<LatLng> latLngArrayList;

    public void storeData(ArrayList<String> names, ArrayList<LatLng> locations) {
        placesList = names;
        latLngArrayList = locations;
    }
}

I am putting them into intent as follows.

            StorageBin storageBin = new StorageBin();
            storageBin.storeData(placesList, latLngArrayList);
            intent.putExtra("storedData", storageBin);

Last line causes following exception. What am I doing wrong?

   FATAL EXCEPTION: main
   Process: lt.wilkas.isimintinosvietoves, PID: 24702
   java.lang.RuntimeException: Parcelable encountered IOException writing
   serializable object (name = lt.wilkas.isimintinosvietoves.MainActivity$StorageBin)
   at android.os.Parcel.writeSerializable(Parcel.java:1468)
   ...

I strongly suspect that it is because your MainActivity class is not serializable (either because it is not declared Serializable , or it has non-serializable field values), and StorageBin is an inner class (ie nested but not static).

Try making StorageBin static:

public static class StorageBin ...

The reason why not being static this breaks the serializability is that StorageBin has a hidden reference to MainActivity : this is what allows you to refer to instance methods on MainActivity (or MainActivity.this ) in the body of StorageBin .

A lot of the time, this reference isn't necessary: not only does it break serialization, it can also cause memory leaks, because it prevents the MainActivity instance being garbage collected.

Always make your nested classes static unless they actually need to be otherwise.


If your use of the LatLng class is also preventing serialization (since it does not implement Serializable ), you have two choices:

  • Stop using the LatLng class, and use a type which is serializable;
    • Either in your public API, so that storeData accepts a list of the other type
    • Make your public API appear to use LatLng , but then translate to a serializable type internally
  • Mark the latLngArrayList field transient , and use writeObject and readObject to provide custom serialization and deserialization logic respectively.

    For example, you can serialize the coordinates to an array, and reconstruct the LatLng instances when you deserialize:

     private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); double[] coords = new double[2 * latLngArrayList.size()]; int i = 0; for (LatLng latLng : latLngArrayList) { coords[2*i+0] = latLng.latitude; coords[2*i+1] = latLng.longitude; ++i; } out.writeObject(coords); } private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { in.defaultReadObject(); double[] coords = (double[]) in.readObject(); // + Check that coords.length is even. latLngArrayList = new ArrayList<>(coords.size() / 2); for (int i = 0; i < coords.size(); i += 2) { latLngArrayList.add(new LatLng(coords[i], coords[i+1])); } } 

Note that implementing Serializable doesn't actually guarantee serializability. You can easily write a class:

class MyClass implements Serializable { NonSerializableType field }

and that compiles fine. And it might serialize, if field is null. Or if it's an instance of subclass of NonSerializableType which does implement Serializable .

I don't want to say that serialization is a guessing game, because it obviously works if used correctly; it's just hard to guarantee that you are using it correctly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM