我正在观察w.r.t传递可序列化数据作为意图额外的行为是非常奇怪的,我只是想澄清是否有一些我不会错过的东西。
所以我试图做的是在ActivtyA
中我将LinkedList
个实例放入我为开始下一个活动而创建的intent
- ActivityB
。< / p>
LinkedList<Item> items = (some operation);
Intent intent = new Intent(this, ActivityB.class);
intent.putExtra(AppConstants.KEY_ITEMS, items);
在onCreate
的{{1}}中,我尝试按以下方式检索ActivityB
附加内容 -
LinkedList
在运行此操作时,我在LinkedList<Item> items = (LinkedList<Item>) getIntent()
.getSerializableExtra(AppConstants.KEY_ITEMS);
的上面一行中反复获得ClassCastException
。基本上,例外说我收到ActivityB
。一旦我将上面的代码更改为接收ArrayList
,一切都运行正常。
现在我不能从现有文档中弄清楚这是否是Android传递可序列化List实现时的预期行为。或者,或许,我正在做的事情存在根本性的错误。
感谢。
答案 0 :(得分:49)
我可以告诉你为什么会这样,但你不会喜欢它; - )
首先是一些背景信息:
Intent
中的 附加内容基本上是Android Bundle
,基本上是HashMap
个键/值对。所以当你做类似的事情时
intent.putExtra(AppConstants.KEY_ITEMS, items);
Android会为附加内容创建新的Bundle
,并向Bundle
添加一个地图条目,其中密钥为AppConstants.KEY_ITEMS
,值为项(其中是你的LinkedList对象。)
这一切都很好,如果你在代码执行后查看extras包,你会发现它包含LinkedList
。现在来了有趣的部分...
当您使用包含额外内容的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);
它会为ArrayList
类型的Bundle
中的所有对象生成List
。
您无法做任何事情来改变Android的这种行为。至少现在你知道它为什么会这样做。
就这样你知道:我实际上写了一个小测试程序来验证这种行为,我查看了Parcel.writeValue(Object v)
的源代码,这是Bundle
转换时调用的方法将地图转换为字节流。
重要说明:由于List
是一个接口,这意味着您在List
中添加Bundle
的任何类都将以ArrayList
形式出现{1}}。
同样有趣的是,Map
也在“已知对象类型”列表中,这意味着无论您将哪个Map
对象放入Bundle
(例如{{1} }},TreeMap
或任何实现SortedMap
接口的类),您将始终获得Map
。
答案 1 :(得分:1)
@David Wasser的答案在诊断问题方面是正确的。这篇文章是为了分享我的处理方式。
任何List
对象作为ArrayList
出现的问题并不可怕,因为您可以随时执行类似
LinkedList<String> items = new LinkedList<>(
(List<String>) intent.getSerializableExtra(KEY));
会将反序列化列表的所有元素添加到新的LinkedList
。
问题在Map
时更糟糕,因为您可能尝试序列化LinkedHashMap
并且现在已经丢失了元素排序。
幸运的是,这是一种(相对)无痛的方法:定义自己的可序列化包装类。您可以针对特定类型执行此操作或一般性地执行此操作:
public class Wrapper <T extends Serializable> implements Serializable {
private T wrapped;
public Wrapper(T wrapped) {
this.wrapped = wrapped;
}
public T get() {
return wrapped;
}
}
然后,您可以使用此功能隐藏您的List
,Map
或Android类型检查中的其他数据类型:
intent.putExtra(KEY, new Wrapper<>(items));
以后:
items = ((Wrapper<LinkedList<String>>) intent.getSerializableExtra(KEY)).get();
答案 2 :(得分:0)
如果您正在使用IcePick库并遇到此问题,您可以将Ted Hoop的技术与自定义捆绑器一起使用,以避免在代码中处理Wrapper实例。
props