将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);

ActivityBonCreate中,我试图检索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序列化为字节流,它将遍历包中的映射并获取每个键/值对。 然后它将每个“值”(这是某种对象)并试图确定它是什么类型的对象,以便它可以以最有效的方式对其进行序列化。 为此,它会根据已知对象类型的列表来检查对象类型 。 “已知对象类型”列表包含诸如IntegerLongStringMapBundle以及不幸的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 (例如TreeMapSortedMap或任何实现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的类型检查中的ListMap或其他数据类型:

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

下一篇: Oracle install within PyDev