将LinkedList放入Intent extra时,会在下次活动中检索时重新映射到ArrayList

我正在观察传递可序列化数据的行为,这是非常奇怪的,我只是想澄清一下,我是不是错过了。

所以我试图做的事情是,在ActivtyA我把一个LinkedList实例到我创build的intent开始下一个活动 – ActivityB

 LinkedList<Item> items = (some operation); Intent intent = new Intent(this, ActivityB.class); intent.putExtra(AppConstants.KEY_ITEMS, items); 

ActivityBonCreate ,我试图检索LinkedList额外如下 –

 LinkedList<Item> items = (LinkedList<Item>) getIntent() .getSerializableExtra(AppConstants.KEY_ITEMS); 

在运行这个,我反复在ActivityB ,在上面的行中得到一个ClassCastException 。 基本上,例外说,我收到一个ArrayList 。 一旦我改变了上面的代码来接收一个ArrayList ,一切正常。

现在我不能从现有的文档中弄清楚,在传递可序列化的List实现时,这是否是Android上的预期行为。 或者也许,我正在做的事情有一些根本性的错误。

谢谢。

Solutions Collecting From Web of "将LinkedList放入Intent extra时,会在下次活动中检索时重新映射到ArrayList"

我可以告诉你为什么会发生这种情况,但是你不会喜欢它;-)

先来一点背景资料:

Intent中的其他部分基本上是一个Android Bundle ,它基本上是一个key / value对的HashMap 。 所以当你做类似的事情

 intent.putExtra(AppConstants.KEY_ITEMS, items); 

Android为extras创build一个新的Bundle ,并将一个映射条目添加到密钥为AppConstants.KEY_ITEMSBundle中,值为items (这是您的LinkedList对象)。

这一切都很好,如果你在代码执行后查看额外的包,你会发现它包含一个LinkedList 。 现在来了有趣的部分…

当您使用包含extras的Intent调用startActivity()时,Android需要将key / value对映射中的额外内容转换为字节stream。 基本上它需要序列化捆绑 。 它需要这样做,因为它可能会在另一个进程中启动活动,为此需要对Bundle中的对象进行序列化/反序列化,以便在新进程中重新创build它们。 还需要这样做,因为Android将Intent的内容保存在某些系统表中,以便在稍后需要时可以重新生成Intent。

为了将Bundle序列化成一个字节stream,它通过包中的映射并获取每个键/值对。 然后它将每个“值”(这是某种对象)并试图确定它是什么types的对象,以便能够以最有效的方式对其进行序列化。 为此,它将检查对象types与已知对象types的列表。 “已知的对象types”列表包含像IntegerLongStringMapBundle ,不幸的还有List 。 所以如果这个对象是一个List (其中有很多不同的types,包括LinkedList ),它将把它序列化并将它标记为Listtypes的对象。

Bundle被反序列化时,即:当你这样做的时候:

 LinkedList<Item> items = (LinkedList<Item>) getIntent().getSerializableExtra(AppConstants.KEY_ITEMS); 

它为Listtypes的Bundle的所有对象生成一个ArrayList

没有任何事情可以改变Android的这种行为。 至less现在你知道它为什么这样做了。

只是为了让你知道:实际上我写了一个小testing程序来validation这个行为,并且查看了Parcel.writeValue(Object v)的源代码,这是从Bundle调用映射到字节时调用的方法stream。

重要说明:由于List是一个接口,这意味着任何实现List类都被放入一个Bundle ,并以ArrayList 。 同样有趣的是, Map也在“已知对象types”列表中,这意味着无论您将哪种Map对象放入一个Bundle (例如TreeMapSortedMap或任何实现Map接口的类)中,您总是会得到一个HashMap

对于问题的诊断, @David Wasser的回答是正确的。 这篇文章是分享我如何处理它。

任何List对象作为ArrayList的问题并不可怕,因为你总是可以做类似的事情

 LinkedList<String> items = new LinkedList<>( (List<String>) intent.getSerializableExtra(KEY)); 

这会将反序列化列表的所有元素添加到一个新的LinkedList

当涉及到Map时候,问题就更糟糕了,因为你可能试图序列化一个LinkedHashMap ,并且现在已经失去了元素sorting。

幸运的是,有一个(相对)无痛的方式:定义你自己的可序列化的包装类。 你可以为特定的types做或一般做:

 public class <T extends Serializable> Wrapper implements Serializable { private T wrapped; public Wrapper(T wrapped) { this.wrapped = wrapped; } public T get() { return wrapped; } } 

然后,您可以使用它来隐藏Android的types检查中的ListMap或其他数据types:

 intent.putExtra(KEY, new Wrapper<>(items)); 

然后:

 items = ((Wrapper<LinkedList<String>>) intent.getSerializableExtra(KEY)).get();