将LinkedList放入Intent extra时,会在下次活动中检索时重新映射到ArrayList
我正在观察传递可序列化数据的行为是否很有意思,我只是想澄清一下,我是不是错过了。
所以我试图做的事情是在ActivtyA
我将LinkedList
实例放入了为创建下一个活动而创建的intent
- ActivityB
。
LinkedList<Item> items = (some operation);
Intent intent = new Intent(this, ActivityB.class);
intent.putExtra(AppConstants.KEY_ITEMS, items);
在ActivityB
的onCreate
中,我试图检索LinkedList
extra,如下所示 -
LinkedList<Item> items = (LinkedList<Item>) getIntent()
.getSerializableExtra(AppConstants.KEY_ITEMS);
在运行这个时,我反复在ActivityB
得到一个ClassCastException
,在上面一行。 基本上,异常说我正在接收ArrayList
。 一旦我改变了上面的代码来接收一个ArrayList
,一切都运行得很好。
现在,我不能从现有的文档中弄清楚,在传递可串行化的List实现时,这是否是Android上的预期行为。 或者,也许,我在做什么有一些根本性的错误。
谢谢。
我可以告诉你为什么会发生这种情况,但你不会喜欢它;-)
首先是一些背景资料:
Intent
中的其他部分基本上是一个Android Bundle
,它基本上是一个键/值对的HashMap
。 所以当你做类似的事情时
intent.putExtra(AppConstants.KEY_ITEMS, items);
Android的创建一个新的Bundle
的演员,并增加了一个映射条目的Bundle
,其中的关键是AppConstants.KEY_ITEMS
和值是一个项目 (这是您的LinkedList的对象)。
这一切都很好,如果你在你的代码执行后看额外包,你会发现它包含一个LinkedList
。 现在来了有趣的部分...
当您使用含extras的Intent调用startActivity()
时,Android需要将来自键/值对映射的额外内容转换为字节流。 基本上它需要序列化Bundle 。 它需要这样做,因为它可能会在另一个进程中启动该活动,并且为此需要对Bundle中的对象进行序列化/反序列化,以便它可以在新进程中重新创建它们。 它也需要这样做,因为Android将Intent的内容保存在某些系统表中,以便在稍后需要时可以重新生成Intent。
为了将Bundle
序列化为字节流,它将遍历包中的映射并获取每个键/值对。 然后它将每个“值”(这是某种对象)并试图确定它是什么类型的对象,以便它可以以最有效的方式对其进行序列化。 为此,它会根据已知对象类型的列表来检查对象类型 。 “已知对象类型”列表包含诸如Integer
, Long
, String
, Map
, Bundle
以及不幸的List
。 因此,如果对象是一个List
(其中有许多不同的种类,包括LinkedList
),它将对它进行序列化并将其标记为List
类型的对象。
当Bundle
被反序列化时,即:当你这样做时:
LinkedList<Item> items = (LinkedList<Item>)
getIntent().getSerializableExtra(AppConstants.KEY_ITEMS);
它为List
类型的Bundle
的所有对象生成一个ArrayList
。
没有什么可以改变Android的这种行为。 至少现在你知道它为什么这样做了。
只是为了让你知道:我实际上编写了一个小测试程序来验证此行为,并且查看了Parcel.writeValue(Object v)
的源代码,该代码是在将映射转换为字节时从Bundle
调用的方法流。
重要提示:由于List
是一个接口,这意味着任何实现List
类都被放入一个Bundle
,并以ArrayList
。 同样有趣的是, Map
也位于“已知对象类型”列表中,这意味着无论您将哪种Map
对象放入Bundle
(例如TreeMap
, SortedMap
或任何实现Map
接口的类)中,您将总是得到一个HashMap
。
在诊断问题方面,@David Wasser的回答是正确的。 这篇文章是为了分享我如何处理它。
任何List
对象作为ArrayList
的问题并不可怕,因为你总是可以做类似的事情
LinkedList<String> items = new LinkedList<>(
(List<String>) intent.getSerializableExtra(KEY));
这会将反序列化列表的所有元素添加到新的LinkedList
。
当涉及到Map
时,问题更加严重,因为您可能试图序列化一个LinkedHashMap
,并且现在已经丢失了元素排序。
幸运的是,这有一个(相对)无痛的方式:定义您自己的可序列化包装类。 您可以针对特定类型执行此操作,也可以一般执行此操作:
public class <T extends Serializable> Wrapper implements Serializable {
private T wrapped;
public Wrapper(T wrapped) {
this.wrapped = wrapped;
}
public T get() {
return wrapped;
}
}
然后,您可以使用它来隐藏Android的类型检查中的List
, Map
或其他数据类型:
intent.putExtra(KEY, new Wrapper<>(items));
然后:
items = ((Wrapper<LinkedList<String>>) intent.getSerializableExtra(KEY)).get();
链接地址: http://www.djcxy.com/p/67897.html
上一篇: LinkedList put into Intent extra gets recast to ArrayList when retrieving in next activity