【问题标题】:Why serialization of object that implements Serializable throws exception?为什么实现 Serializable 的对象序列化会抛出异常?
【发布时间】:2016-08-05 07:32:00
【问题描述】:

我正在尝试通过 Intent 将两个 ArrayLists 传递给另一个活动。创建了这个类来存储这两个数组。

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;
    }
}

我将它们置于如下意图中。

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

最后一行导致以下异常。我做错了什么?

   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)
   ...

【问题讨论】:

  • Serializable 实际上并不能保证可序列化。您可以轻松地编写一个 class MyClass implements Serializable { NonSerializableType field } 的类,并且编译得很好。如果field 为空,它可能 序列化。或者,如果它是 NonSerializableType 的子类实例,它确实实现了 Serializable
  • MainActivity$StorageBin 表示您的StorageBin 嵌套在MainActivity 类中,但您没有创建嵌套类static,所以它是一个inner类,这意味着它正在尝试序列化整个MainActivity 类。
  • 我是否理解正确,如果我的可序列化类中有一个不可序列化的对象,那么我的类也变得不可序列化?
  • @wilkas 正确。我认为Serializable 是您希望类可序列化的标记,而不是保证它可以序列化;而两者之间的区别感觉就像是 Java 内置序列化设计的缺陷。

标签: java android serialization


【解决方案1】:

我强烈怀疑这是因为您的MainActivity 类不可序列化(或者因为它没有声明Serializable,或者它具有不可序列化的字段值),而StorageBin 是一个内部类(即嵌套但不是静态的)。

尝试将StorageBin设为静态:

public static class StorageBin ...

之所以不是静态的,这会破坏可序列化性,是因为StorageBin 有一个对MainActivity 的隐藏引用:这允许您在正文中引用MainActivity(或MainActivity.this)上的实例方法StorageBin.

很多时候,这个引用不是必需的:它不仅会破坏序列化,还会导致内存泄漏,因为它可以防止 MainActivity 实例被垃圾回收。

总是让你的嵌套类static,除非他们真的需要这样做。


如果您使用LatLng class 也阻止了序列化(因为它没有实现Serializable),您有两种选择:

  • 停止使用LatLng 类,并使用可序列化的类型;
    • 在您的公共 API 中,以便 storeData 接受其他类型的列表
    • 使您的公共 API 看起来使用 LatLng,但随后在内部转换为可序列化类型
  • 标记latLngArrayList字段transient,并使用writeObjectreadObject分别提供自定义序列化和反序列化逻辑。

    例如,您可以将坐标序列化为数组,并在反序列化时重构LatLng 实例:

    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]));
      }
    }
    

请注意,实现Serializable 实际上并不能保证可串行化。您可以轻松编写一个类:

class MyClass implements Serializable { NonSerializableType field }

编译得很好。如果field 为空,它可能会序列化。或者,如果它是 NonSerializableType 的子类实例,它确实实现了 Serializable

我不想说序列化是一个猜谜游戏,因为如果使用得当,它显然是有效的;很难保证您正确使用它。

【讨论】:

  • MainActivity$StorageBin 这个名字不是让您的怀疑成为必然吗? ;-)
  • @Andreas 不确定 - 你怎么知道 MainActivity 没有声明 Serializable :)
  • 我尝试在定义中添加“静态”,但得到了同样的错误。之后尝试将此类移到 MainActivity 类之外(也删除“公共静态”),但随后异常如下: java.lang.RuntimeException:Parcelable 遇到 IOException 写入可序列化对象(名称 = lt.wilkas.isimintinosvietoves.StorageBin)我认为它可能与我未声明的“LatLng”类有关。
  • 是的,就是这样。删除 LatLng 对象数组后,序列化工作。这让我有疑问如何将这个数组传递给另一个活动:(
  • @ishmaelMakitl,您的行允许我将 'storageBin' 放入 'bundle',但是一旦我将 bundle 作为 Intent extra,它给了我同样的例外。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-03
  • 2021-04-28
相关资源
最近更新 更多