I'm using the Navigation Component version 2.1.0-rc01
and I navigate back and forth between 3 screens using
Navigation.findNavController(it).navigate(R.id.action_participants)
After going through the same screens a second time I can see the second fragment but I receive an exception. I enabled the log on the FragmentManager and it seems like there is a difference instance of the same fragment that is not attached, causing the error
Any ideas on why the Navigation Component is creating another instance of the fragment that is not being attached? Any workarounds to get the attached fragment instead?
2019-08-15 16:59:30.895 30041-30041/com.app.debug D/FragmentManager: mName=3-2131361912 mIndex=-1 mCommitted=false
2019-08-15 16:59:30.895 30041-30041/com.app.debug D/FragmentManager: mEnterAnim=#7f01001e mExitAnim=#7f01001f
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: mPopEnterAnim=#7f010020 mPopExitAnim=#7f010021
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: Operations:
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: Op #0: REPLACE StaffBookingDetailsFragment{82e8301 (97f79b28-d8c1-432a-9e1c-3a781dd42434) id=0x7f0a01c5}
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: enterAnim=#7f01001e exitAnim=#7f01001f
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: popEnterAnim=#7f010020 popExitAnim=#7f010021
2019-08-15 16:59:30.896 30041-30041/com.app.debug D/FragmentManager: Op #1: SET_PRIMARY_NAV StaffBookingDetailsFragment{82e8301 (97f79b28-d8c1-432a-9e1c-3a781dd42434) id=0x7f0a01c5}
2019-08-15 16:59:30.897 30041-30041/com.app.debug D/FragmentManager: enterAnim=#7f01001e exitAnim=#7f01001f
2019-08-15 16:59:30.897 30041-30041/com.app.debug D/FragmentManager: popEnterAnim=#7f010020 popExitAnim=#7f010021
2019-08-15 16:59:31.935 30041-30041/com.app.debug D/FragmentManager: mName=4-2131362286 mIndex=-1 mCommitted=false
2019-08-15 16:59:31.935 30041-30041/com.app.debug D/FragmentManager: Operations:
2019-08-15 16:59:31.936 30041-30041/com.app.debug D/FragmentManager: Op #0: REPLACE ParticipantsFragment{fdd9ef9 (b7317713-b150-44a2-8b1c-47a0f8c52781) id=0x7f0a01c5}
2019-08-15 16:59:31.936 30041-30041/com.app.debug D/FragmentManager: Op #1: SET_PRIMARY_NAV ParticipantsFragment{fdd9ef9 (b7317713-b150-44a2-8b1c-47a0f8c52781) id=0x7f0a01c5}
2019-08-15 16:59:55.266 30041-30041/com.app.debug E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.app.debug, PID: 30041
java.lang.IllegalStateException: Fragment ParticipantsFragment{b6e8bc7 (aa204a1e-5f3a-40c0-86f0-b5edab4b07eb)} not associated with a fragment manager.
at androidx.fragment.app.Fragment.requireFragmentManager(Fragment.java:910)
at com.app.bookings.participants.ParticipantsFragment.onParticipantActionClicked(ParticipantsFragment.kt:88)
at com.app.databinding.ItemBindParticipantBindingImpl._internalCallbackOnClick(ItemBindParticipantBindingImpl.java:218)
at com.app.generated.callback.OnClickListener.onClick(OnClickListener.java:11)
at android.view.View.performClick(View.java:6669)
at android.view.View.performClickInternal(View.java:6638)
at android.view.View.access$3100(View.java:789)
at android.view.View$PerformClick.run(View.java:26145)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6863)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Make sure that the Fragment is garbage collected/destroyed. Fragment will not be garbage collected/destroyed if any lifecycle unaware registered listeners (listeners that do not support androidx.lifecycle.Lifecycle) are registered in onCreateView/onViewCreated etc methods. Make sure that you unregister such listeners in onDestroyView() of fragment.
Example :
OnBackPressedDispatcher
is not lifecycle aware. Therefore it expects you to unregister when that fragment is destroyed. If it is not unregistered then it keeps a reference and gets called when back is pressed in some other fragment also.
I was calling findNavController().navigateUp() inside
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
//some logic that needs to be run before fragment is destroyed
findNavController().navigateUp()
}
}
requireActivity().onBackPressedDispatcher.addCallback(
onBackPressedCallback
)
}
and if you look at documentation of findNavController()
Calling this on a Fragment that is not a [NavHostFragment] or within a [NavHostFragment] will result in an [IllegalStateException]
This is why I was getting
IllegalStateException Fragment not associated with a fragment manager
Solution :
Unregister Listeners in onDestroyView
override fun onDestroyView() {
super.onDestroyView()
//unregister listener here
onBackPressedCallback.isEnabled = false
onBackPressedCallback.remove()
}
If add the lifecycle Owner, you do not need to remove the callback in onDestroy
Documentation
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
findNavController().navigateUp()
}
}
// ADD LIFECYCLE OWNER
requireActivity().onBackPressedDispatcher.addCallback(this,
onBackPressedCallback
)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With