如何通过 ViewModel 在片段之间正确共享数据?
How to correctly share data between Fragements over ViewModel?
在我的应用程序中,我使用导航组件,还使用 ViewModel
在片段之间共享数据。这是我的场景,片段 A
、片段 B
和片段 C
。 A
与 B
共享字符串数据,B
与 C
共享,而在 C
中用户编辑共享字符串并返回到 B
与编辑的字符串数据。我通过 ViewModel
class 共享数据,如下所示:
class Share : ViewModel() {
val shared = MutableLiveData<String>()
fun share(share: String?) {
shared.value = share!!
}
}
从 A
我像这样将字符串发送到 B
...
private val share: Share by activityViewModels()
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
// Let's say, this is executed in some onClickListener {}
share.share("My String for B!")
findNavController().navigate(R.id.action_nav_a_to_nav_b)
...
}
...
根据 Google,要在 B
中接收共享字符串,我必须观察这样的数据:
...
private var sharedString = ""
private val share: Share by activityViewModels()
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
share.shared.observe(viewLifecycleOwner, { // shared String })
...
}
第一个问题:
我无法访问 observe
来获取这样的字符串:
...
var sharedString = ""
share.shared.observe(viewLifecycleOwner, {
sharedString = it // The value is not assigned!
})
println(sharedString) // This prints nothing!
...
因此,我必须调用一个方法才能访问它:
share.shared.observe(viewLifecycleOwner, { get(it) })
...
private fun get(string: String) {
println(string) // This prints the shared string "My String for B!"!
}
下一个问题是,根据该方法,有时应用程序会崩溃:
java.lang.NullPointerException:
at com.myapp.BFragment.get (BFragment.java:45) // -> this is the line of share.shared.observe(viewLifecycleOwner, { get(it) })
at com.myapp.BFragment.onViewCreated$lambda-3 (BFragment.java:25) // -> this is the line of private val share: Share by activityViewModels()
at com.myapp.BFragment$$InternalSyntheticLambda[=15=]$e6af021286f0d4217981b099cd8f3b0ac57de6c0ebe35bee4010f87e07a6ecac.onChanged (BFragment.java)
at androidx.lifecycle.LiveData.considerNotify (LiveData.java:133)
at androidx.lifecycle.LiveData.dispatchingValue (LiveData.java:146)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged (LiveData.java:468)
at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged (LiveData.java:425)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent (LifecycleRegistry.java:354)
at androidx.lifecycle.LifecycleRegistry.forwardPass (LifecycleRegistry.java:265)
at androidx.lifecycle.LifecycleRegistry.sync (LifecycleRegistry.java:307)
at androidx.lifecycle.LifecycleRegistry.moveToState (LifecycleRegistry.java:148)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent (LifecycleRegistry.java:134)
at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent (FragmentViewLifecycleOwner.java:88)
at androidx.fragment.app.Fragment.performStart (Fragment.java:3028)
at androidx.fragment.app.FragmentStateManager.start (FragmentStateManager.java:589)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState (FragmentStateManager.java:300)
at androidx.fragment.app.FragmentManager.executeOpsTogether (FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute (FragmentManager.java:2106)
at androidx.fragment.app.FragmentManager.execPendingActions (FragmentManager.java:2002)
at androidx.fragment.app.FragmentManager.run (FragmentManager.java:524)
at android.os.Handler.handleCallback (Handler.java:938)
at android.os.Handler.dispatchMessage (Handler.java:99)
at android.os.Looper.loop (Looper.java:246)
at android.app.ActivityThread.main (ActivityThread.java:8587)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1130)
接下来,我将该字符串分享给 C
并在那里编辑:
...
// Let's say again, this is executed in some onClickListener {}
share.share(sharedString)
findNavController().navigate(R.id.action_nav_b_to_nav_c)
...
在 C
中,我是这样理解的:
private val share: Share by activityViewModels()
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
val string = share.shared.value!! // As you can see without calling observe.
// Now we change the value and send it back
share.share("My new string for B!")
findNavController().navigateUp()
...
}
问题:
- 在这种情况下,最好的方法是什么,是像
B
中那样观察共享数据,还是像 C
中那样直接获取价值?
- 为什么应用有时会崩溃?
- 我是不是做错了或者错过了什么?
我认为 Google 的方法不是最好的解决方案。我认为他们必须带来一些不同的更好的解决方案,以允许在发布导航组件之前在片段之间共享数据。这对我来说似乎没有效果。
尽管如此,我正在努力让它毫无例外地完成工作。
我搜索了一个解决方案,也试图找到那个异常的问题,但还没有成功。
由于您正在使用 jetpack 导航组件,您可以尝试通过 nav args(导航参数)传递 objects/strings。
我个人觉得在片段之间传递数据要容易得多。
文章:https://medium.com/androiddevelopers/navigating-with-safeargs-bf26c17b1269
您需要向具有数据类型和默认值的目标片段添加参数。
因此,当您导航时,您可以像这样添加参数并从目标片段中获取值,
private val dataFromArgument
by lazy { DestinationFragmentArgs.fromBundle(requireArguments()).variableYouPassed}
在我的应用程序中,我使用导航组件,还使用 ViewModel
在片段之间共享数据。这是我的场景,片段 A
、片段 B
和片段 C
。 A
与 B
共享字符串数据,B
与 C
共享,而在 C
中用户编辑共享字符串并返回到 B
与编辑的字符串数据。我通过 ViewModel
class 共享数据,如下所示:
class Share : ViewModel() {
val shared = MutableLiveData<String>()
fun share(share: String?) {
shared.value = share!!
}
}
从 A
我像这样将字符串发送到 B
...
private val share: Share by activityViewModels()
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
// Let's say, this is executed in some onClickListener {}
share.share("My String for B!")
findNavController().navigate(R.id.action_nav_a_to_nav_b)
...
}
...
根据 Google,要在 B
中接收共享字符串,我必须观察这样的数据:
...
private var sharedString = ""
private val share: Share by activityViewModels()
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
share.shared.observe(viewLifecycleOwner, { // shared String })
...
}
第一个问题:
我无法访问 observe
来获取这样的字符串:
...
var sharedString = ""
share.shared.observe(viewLifecycleOwner, {
sharedString = it // The value is not assigned!
})
println(sharedString) // This prints nothing!
...
因此,我必须调用一个方法才能访问它:
share.shared.observe(viewLifecycleOwner, { get(it) })
...
private fun get(string: String) {
println(string) // This prints the shared string "My String for B!"!
}
下一个问题是,根据该方法,有时应用程序会崩溃:
java.lang.NullPointerException:
at com.myapp.BFragment.get (BFragment.java:45) // -> this is the line of share.shared.observe(viewLifecycleOwner, { get(it) })
at com.myapp.BFragment.onViewCreated$lambda-3 (BFragment.java:25) // -> this is the line of private val share: Share by activityViewModels()
at com.myapp.BFragment$$InternalSyntheticLambda[=15=]$e6af021286f0d4217981b099cd8f3b0ac57de6c0ebe35bee4010f87e07a6ecac.onChanged (BFragment.java)
at androidx.lifecycle.LiveData.considerNotify (LiveData.java:133)
at androidx.lifecycle.LiveData.dispatchingValue (LiveData.java:146)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged (LiveData.java:468)
at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged (LiveData.java:425)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent (LifecycleRegistry.java:354)
at androidx.lifecycle.LifecycleRegistry.forwardPass (LifecycleRegistry.java:265)
at androidx.lifecycle.LifecycleRegistry.sync (LifecycleRegistry.java:307)
at androidx.lifecycle.LifecycleRegistry.moveToState (LifecycleRegistry.java:148)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent (LifecycleRegistry.java:134)
at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent (FragmentViewLifecycleOwner.java:88)
at androidx.fragment.app.Fragment.performStart (Fragment.java:3028)
at androidx.fragment.app.FragmentStateManager.start (FragmentStateManager.java:589)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState (FragmentStateManager.java:300)
at androidx.fragment.app.FragmentManager.executeOpsTogether (FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute (FragmentManager.java:2106)
at androidx.fragment.app.FragmentManager.execPendingActions (FragmentManager.java:2002)
at androidx.fragment.app.FragmentManager.run (FragmentManager.java:524)
at android.os.Handler.handleCallback (Handler.java:938)
at android.os.Handler.dispatchMessage (Handler.java:99)
at android.os.Looper.loop (Looper.java:246)
at android.app.ActivityThread.main (ActivityThread.java:8587)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1130)
接下来,我将该字符串分享给 C
并在那里编辑:
...
// Let's say again, this is executed in some onClickListener {}
share.share(sharedString)
findNavController().navigate(R.id.action_nav_b_to_nav_c)
...
在 C
中,我是这样理解的:
private val share: Share by activityViewModels()
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
val string = share.shared.value!! // As you can see without calling observe.
// Now we change the value and send it back
share.share("My new string for B!")
findNavController().navigateUp()
...
}
问题:
- 在这种情况下,最好的方法是什么,是像
B
中那样观察共享数据,还是像C
中那样直接获取价值? - 为什么应用有时会崩溃?
- 我是不是做错了或者错过了什么?
我认为 Google 的方法不是最好的解决方案。我认为他们必须带来一些不同的更好的解决方案,以允许在发布导航组件之前在片段之间共享数据。这对我来说似乎没有效果。
尽管如此,我正在努力让它毫无例外地完成工作。
我搜索了一个解决方案,也试图找到那个异常的问题,但还没有成功。
由于您正在使用 jetpack 导航组件,您可以尝试通过 nav args(导航参数)传递 objects/strings。 我个人觉得在片段之间传递数据要容易得多。 文章:https://medium.com/androiddevelopers/navigating-with-safeargs-bf26c17b1269
您需要向具有数据类型和默认值的目标片段添加参数。 因此,当您导航时,您可以像这样添加参数并从目标片段中获取值,
private val dataFromArgument
by lazy { DestinationFragmentArgs.fromBundle(requireArguments()).variableYouPassed}