将对象发送到另一个 Fragment 后对象发生更改

Object is changed after sending it to another Fragment

所以,我有一个非常奇怪的问题,我将一个对象从 Fragment A 传递到 Fragment B ,我在Fragment B 中的一个新实例,但在我更改此对象的值后,当我弹出 Framgment B 时它也会更改该值并且该对象现在也为 Fragment 保持修改A

片段A

...

   override fun onItemClick(v: View?, position: Int) {
        searchView.clearFocus()
        val bundle = Bundle()
        bundle.putSerializable("shop", landingAdapter.getItem(position))
        findNavController().navigate(R.id.action_navigation_landing_to_shopFragment, bundle)
    }

...

现在,我从片段 B 得到这个对象

片段 B

    private lateinit var shop: Shop
    private lateinit var shopAdapter:ShopAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        shopAdapter = ShopAdapter(sharedViewModel, requireContext(),this)
        arguments?.let {
             shop = it.getSerializable(ARG_SHOP) as Shop
            if (shop.products.isNotEmpty()) {
                shopAdapter.setItems(shop.products)
            }
        }
    }

现在,在我从 Fragment A 中获取这个 Shop 对象后,我只在 Fragment B 中修改它

onViewCreated(){

    shop.quantity = 1

}

但是当我回到 Fragment A 时,现在 Shop 对象的数量值为 1 ,但它应该没什么,因为我只更改了 Fragment B 处的对象而不是 Fragment A ,并且在 Fragment B 中是一个新实例该对象的

我真的很困惑

编辑

到目前为止,我每次从片段 A 转到片段 b 时都尝试发送一个新实例

   val bundle = Bundle()
                bundle.putSerializable("shop", landingAdapter.getItem(position).copy())  

findNavController().navigate(R.id.action_navigation_landing_to_shopFragment, bundle)



         val bundle = Bundle()
                val shop = landingAdapter.getItem(position)
                bundle.putSerializable("shop", shop)  findNavController().navigate(R.id.action_navigation_landing_to_shopFragment, bundle)


         val bundle = Bundle()
                val shop = Shop(landingAdapter.getItem(position).name,landingAdapter.getItem(position).quantity)
                bundle.putSerializable("shop", shop)  
    findNavController().navigate(R.id.action_navigation_landing_to_shopFragment, bundle)

None 他们将商店的新实例发送到片段 B,因此每当我更改片段 B 的数量时,片段 A 都会获得相同的数量值,该值不应发生变化

对象引用按值传递

Kotlin 中的所有对象引用都是按值传递的。这意味着值的副本将传递给方法。但诀窍在于传递值的副本也会更改对象的实际值。要了解原因,请尝试以下示例:

object  ObjectReferenceExample {
 fun transform(p:Person) {
        p.name = "Person2";
 }
}

class Person(var name:String) 
   fun main() {
     val  person =  Person("Person1");
     ObjectReferenceExample.transform(person);
     System.out.println(person.name);// output are Person2
}

原因是 Kotlin 对象变量只是指向内存堆中真实对象的引用。因此,即使 Kotlin 将参数按值传递给方法,如果变量指向一个对象引用,真实的对象也会被改变。 同样适用于java

这其实不是一个有明显答案的明显问题。我大约 2 个月前收到这个问题,它也让我感到困惑,因为这有时是你得到的行为,有时不是。

首先要注意的是,当您为片段提供参数时,您将它们放在一个 Bundle 中。这个 bundle 在内部为字符串键存储了一个 Map。

所以当你调用 fragment.setArguments(bundle) 时,你基本上是 "sending a map"。

因此,由于没有发生 IPC(不像在 Activity 中,您通过 Intent 与 Android 对话),地图中存在相同的对象实例。

也就是直到Bundle参数的属性被系统使用,并且确实重新创建了实例

进程终止后(您的应用程序在后台,Android 回收内存,并在重新启动时 Android 重建您的任务堆栈并根据保存到 onSaveInstanceState 的现有历史记录重新创建活动片段FragmentManager),最初传递的参数用于"recreate"传入的属性。

这时候Serializable被系统重新创建了,如果你再导航回来,它会和你最初发送的实例不同。

因此,您得到了相同的实例,因为 Bundle 内部是一个映射。

您还可以获得不同的实例,因为 Android 可以重新创建它。

解决方案:在发送您的实例之前使用一个副本以获得一致的行为。

编辑:

这也适用于可变列表中的嵌套对象。因此,如果你有类喜欢

class A(
    private val list: List<B>
)

class B(
    private var blah: String
)

然后,如果 B 在通过 Bundle 发送 A 之后发生了变异,那么 List<B> 中的 B 会发生变化,这将反映在两个屏幕,除非在进程死亡后。

所以要记住这一点。

要创建副本,您可以这样做

val copyList = a.list.map { it.copy() }