将对象发送到另一个 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() }
所以,我有一个非常奇怪的问题,我将一个对象从 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() }