将对象发送到另一个片段后进行更改 [英] Object is changed after sending it to another Fragment

本文介绍了将对象发送到另一个片段后进行更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,我有一个很奇怪的问题,我将一个对象从片段A 传递给片段B ,然后在片段B的新实例中修改了该对象,但是更改此对象的值后,当我弹出片段B 时,它也会更改该值,并且该对象现在也针对片段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)
            }
        }
    }

现在,从片段A获取此Shop对象后,仅使用

在片段B中对其进行修改

onViewCreated(){

    shop.quantity = 1

}

但是当我返回到片段A时,现在Shop对象数量值是1,但是应该不算什么,因为我只更改了Fragment B上的对象,而不是Fragment A,并且在Fragment B中是该对象的新实例对象

我真的很困惑

编辑

到目前为止,我每次尝试从Fragment A到Fragment 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)

他们中没有一个将新的shop实例发送到片段B,因此,每当我更改片段B的数量时,片段A都会获得相同的数量值,不应更改

解决方案

这实际上不是一个带有明显答案的明显问题.大约2个月前,我收到了这个问题,这也使我感到困惑,因为有时这是您的行为,有时不是.

首先要注意的是,当您给Fragment提供参数时,会将它们放在Bundle中.该捆绑包在内部存储了一个用于字符串键的Map.

因此,当您呼叫fragment.setArguments(bundle)时,基本上就是在发送地图".

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

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

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

这时,系统重新创建了Serializable,如果您要导航回去,它将是与最初发送的实例不同的实例.

因此,您将获得相同的实例,因为Bundle在内部是地图.

您还可以获取其他实例,因为Android可以重新创建它.

解决方案:在发送实例之前,请使用副本以保持一致的行为.

这也适用于可变列表内的嵌套对象.因此,如果您有类似的课程

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

还有

class B(
    private var blah: String
)

然后,如果通过捆绑包发送了A之后对B进行了更改,则List<B>中的B会更改,并且这将在两个屏幕上反映出来,除非进程死亡. >

这是要记住的事情.

要创建副本,您可以

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

So, I have a really weird issue, I pass an object from Fragment A to Fragment B , I modify this object in a new instance in Fragment B, but after I change a value on this object it also changes that value when I pop Framgment B and that object keeps modified now also for Fragment A

Fragment 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)
    }

...

Now, from Fragment B I get this object

Fragment 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)
            }
        }
    }

Now, after I get this Shop object from Fragment A, I modify it in Fragment B only with

onViewCreated(){

    shop.quantity = 1

}

but when I go back to Fragment A, now that Shop object quantity value is 1 , but it should be nothing since I have only changed the object at Fragment B not Fragment A , and in Fragment B is a new instance of that object

I'm really confused

EDIT

What I have tried so far to send a fresh instance each time I go from Fragment A to Fragment 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 of them sends a fresh instance of shop to Fragment B, so whenever I change quantity at fragment B, fragment A gets the same quantity value which should not mutate

解决方案

This is actually not an obvious question with an obvious answer. I got this question about 2 months ago and it confused me as well, as this is sometimes the behavior you get, and sometimes not.

First thing to note is, when you give arguments to a Fragment, you put them in a Bundle. This bundle internally stores a Map for string keys.

So when you call fragment.setArguments(bundle), you're basically "sending a map".

Therefore, as no IPC happens (unlike in Activities, where you talk to Android through an Intent), the same object instance exists in the map.

That is until the properties of the Bundle arguments are used by the system, and the instance is indeed recreated.

After process death (you have the app in background, Android reclaims for memory, and on restart Android rebuilds your task stack and recreates the active Fragments based on existing history saved into onSaveInstanceState from the FragmentManager), the originally passed arguments are used to "recreate" the incoming properties.

At this time, the Serializable was recreated by the system, and if you were to navigate back, it would be a different instance than the one you had originally sent.

Therefore, you get the same instance because Bundle is internally a map.

You can also get a different instance because Android can recreate it.

Solution: use a copy before sending your instance for consistent behavior.

EDIT:

This also applies for nested objects inside mutable lists. Therefore, if you have classes like

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

And

class B(
    private var blah: String
)

Then if B is mutated after sending A over through the Bundle, then the List<B> has the Bs in it change, and this will reflect on both screen, UNLESS after process death.

So that's something to keep in mind.

To create a copy, you could do

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

这篇关于将对象发送到另一个片段后进行更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆