Issue
In my app I use Navigation Component and also, use ViewModel
to share data between Fragments. This is my scenario, Fragment A
, Fragment B
, and Fragment C
. A
shares string data with B
and B
shares it with C
, where as in C
the user edit the shared string and goes back to B
with edited string data. I share data over a ViewModel
class like this:
class Share : ViewModel() {
val shared = MutableLiveData<String>()
fun share(share: String?) {
shared.value = share!!
}
}
From A
I send the string to B
like this
...
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)
...
}
...
According to Google, to receive shared string in B
, I have to observe data like this:
...
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 })
...
}
First problem:
I cannot access the observe
to get the string like this:
...
var sharedString = ""
share.shared.observe(viewLifecycleOwner, {
sharedString = it // The value is not assigned!
})
println(sharedString) // This prints nothing!
...
Therefore, I have to call a method to be able to access it:
share.shared.observe(viewLifecycleOwner, { get(it) })
...
private fun get(string: String) {
println(string) // This prints the shared string "My String for B!"!
}
The next problem is that, according to that approach, sometimes app crashes:
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$0$e6af021286f0d4217981b099cd8f3b0ac57de6c0ebe35bee4010f87e07a6ecac$2.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$5.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)
The next thing, I share that string to C
and edit there:
...
// Let's say again, this is executed in some onClickListener {}
share.share(sharedString)
findNavController().navigate(R.id.action_nav_b_to_nav_c)
...
And in C
I get it like this:
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()
...
}
Questions:
- What is the best approach in this case, to observe the shared data like in
B
or get directly over value like inC
? - Why does the app sometimes crash?
- Do I do it in wrong way or miss something?
I do not think that the Google's approach is the best solution. I think they had to bring something different and better solution to allow data sharing between fragments before releasing the navigation component. That does not look effective to me.
Nevertheless, I am trying to get it done working without exceptions.
I searched for a solution and also, tried to find the problem with that exception, but no success yet.
Solution
As you are using jetpack navigation component, you can try passing objects/strings via nav args (navigation arguments). Personally, I feel it to be lot more easier when passing data between fragments. Article: https://medium.com/androiddevelopers/navigating-with-safeargs-bf26c17b1269
You need to add arguments to destination fragment with data type and default value. So when you navigation, you can add the argument and fetch the value from destination fragment like this,
private val dataFromArgument
by lazy { DestinationFragmentArgs.fromBundle(requireArguments()).variableYouPassed}
Answered By - Ankush
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.