通过Bundle检索Parcelable对象是否总是创建新副本?

16

我在创建Fragment时,通过将Parcelable对象添加到Bundle中来传递它。在某些情况下,对这个Parcelable对象的修改会反映在原始对象中,而在另一些情况下则不会。这种行为让我有点困惑。到目前为止,我一直认为通过Bundle获取Parcelable对象总是会创建新对象[不确定是浅拷贝还是深拷贝]。

请有经验的人解释一下Parcelable的行为。

1个回答

26

我曾经遇到过类似的问题。乍一看,我们总是会从打包的对象中获得一个新的深复制。此外,甚至还有一些StackOverflow的答案建议使用Parcelable接口来克隆对象。这一切只会增加关于这个主题的困惑。

在大量搜索和谷歌之后,我找到了以下内容:

  • 仔细查看官方Parcel文档。这里是重要的引用:

Parcel的一个不寻常的特性是读取和写入活动对象。对于这些对象,实际对象的内容没有被写入,而是写入了引用对象的特殊标记。从Parcel读回对象时,您不会得到对象的新实例,而是获得一个操作最初写入的完全相同对象的句柄。

正如您所看到的,有一些特殊的对象在解包期间没有被复制。但这仍然有点让人困惑。这是否意味着我们有了原始对象的另一个强引用,从而防止其垃圾回收?这些对象的用例是什么?

  • 为了回答上述问题,我决定查看Android源代码。我寻找的方法是readStrongBinderwriteStrongBinder,根据文档,它们在发送/接收包时不会导致新对象创建。我认为我在ResultReceiver.java类中找到了想要的答案。这里是一个有趣的行:

    mReceiver = IResultReceiver.Stub.asInterface(in.readStrongBinder());
    
    为了理解这行代码实际在做什么,我们需要查看官方的AIDL文档。以下是其中最重要的部分:
    调用使用AIDL定义的远程接口时,调用类必须执行以下步骤: ... 5. 在onServiceConnected()方法的实现中,您将收到一个IBinder实例(称为service)。调用YourInterfaceName.Stub.asInterface((IBinder)service)将返回参数转换为YourInterface类型。
    关于调用IPC服务的一些说明:
    对象在进程之间进行引用计数
    因此,让我们把所有东西放在一起:
    1. 封送的对象可以在不涉及深度复制过程的情况下提取出来。 2. 如果通过readStrongBinder方法读取封送的对象,则不会创建新的实例。我们只是获得对原始对象的新引用,并且该引用可以防止其被释放。 3. 要知道在接收到封包后我们的对象是否会被深度复制,我们应该更详细地研究具体的Parcelable接口实现。 4. Android文档可能真的很令人困惑,理解正确可能需要花费很多时间。
    希望这些信息能够帮助您。
    如果您想阅读一个真实的例子来了解有关Parcelable对象的混淆可能会导致严重问题,请查看我的博客文章

这是我的测试结果。如果我们通过Intent+Parcelable将一个对象传递给Activity,它会按值传递。如果我们通过Parcelable将一个对象传递给Fragment,它会按引用传递。但是,我认为这种行为并不是保证的,因为它没有被记录下来。 - Cheok Yan Cheng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接