'Navigation Component: IllegalStateException Fragment not associated with a fragment manager

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)


Solution 1:[1]

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

Solution 2:[2]

If add the lifecycle Owner, you do not need to remove the callback in onDestroyDocumentation

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

Solution 3:[3]

You can use the XML property popUpToInclusive="true" of the navigation action to specify if old instances of the same destination should be popped. Also see the documentation

Solution 4:[4]

Navigation.findNavController(it).navigate(R.id.action_participants)

Instead of the above place

Navigation.findNavController(context).navigate(R.id.action_participants)

Solution 5:[5]

I found that you used viewbinding. This class was created in the adapter and cannot directly use findnavcontroller. You need to pass the current fragment to the adapter, and then pass the past fragment to the required class. If you need activity, the same is true. Pass the requireactivity to the adapter and then to the required class

Solution 6:[6]

Upon further investigation, I verified that this is just a side effect when the fragment is not disposed properly. It is solved now.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 sadat
Solution 3 Maximilian Speicher
Solution 4 srinivasu
Solution 5 ????
Solution 6 Leonardo Deleon