Android Navigation Error: java.lang.IllegalArgumentException: navigation destination com.example--- is unknown to this NavController

Viewed 2766

I am getting an Error: java.lang.IllegalArgumentException, the error occurs only when i navigate to RegisterFragment from LoginFragment and then press back button to go to previous fragment(LoginFragment) and enter email and password and press login button. When i comment the line view!!.findNavController().navigate(LoginFragmentDirections.actionLoginFragmentToHomeContainerFragment()), the error doesn't occur, but i have to navigate to HomeFragment. How to fix it?

Note: The Error doesn't occur when you don't navigate to RegisterFragment

LoginFragment:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
{
    val binding: FragmentLoginBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_login, container, false)

    val login = binding.loginBtn
    val emailField = binding.inputEmail
    val passwordField = binding.inputPassword
    val signUp = binding.loginSignup

    binding.viewModel = viewModel
    binding.lifecycleOwner = this

    signUp.setOnClickListener {
        view!!.findNavController().navigate(LoginFragmentDirections.actionLoginFragmentToRegisterFragment())
    }

    login.setOnClickListener {
        val email = emailField.text.toString().trim()
        val password = passwordField.text.toString().trim()

        if(email.isEmpty())
        {
            .................
        }

        if(!Patterns.EMAIL_ADDRESS.matcher(email).matches())
        {
            .................
        }

        viewModel.userLogin(email,password)
    }

    viewModel.loginAuthData.observe(this, Observer { userAuthData ->
        if(userAuthData.checkAuth != null)
        {
            if(userAuthData.checkAuth!!)
            {
                Snackbar.make(activity!!.findViewById(android.R.id.content), "Auth Pass", Snackbar.LENGTH_SHORT).show()
                //The error doesn't occur if i comment this line >>
                view!!.findNavController().navigate(LoginFragmentDirections.actionLoginFragmentToHomeContainerFragment())
            }
            else
            {
                Snackbar.make(activity!!.findViewById(android.R.id.content), "Auth Fail", Snackbar.LENGTH_SHORT).show()
            }
        }
    })

    return binding.root
}

Error:

  Process: com.example.---, PID: 24721
  java.lang.IllegalArgumentException: navigation destination com.example.---:id/action_loginFragment_to_homeContainerFragment is unknown to this NavController
    at androidx.navigation.NavController.navigate(NavController.java:789)
    at androidx.navigation.NavController.navigate(NavController.java:730)
    at androidx.navigation.NavController.navigate(NavController.java:716)
    at androidx.navigation.NavController.navigate(NavController.java:907)
    at com.example.---.ui.login.LoginFragment$onCreateView$3.onChanged(LoginFragment.kt:71)
    at com.example.---.ui.login.LoginFragment$onCreateView$3.onChanged(LoginFragment.kt:20)
    at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
    at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
    at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
    at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
    at com.example.---.viewModel.LoginViewModel.authCallBack(LoginViewModel.kt:37)
    at com.example.---.viewModel.LoginViewModel.access$authCallBack(LoginViewModel.kt:12)
    at com.example.---.viewModel.LoginViewModel$firebaseUserAuth$1.invoke(LoginViewModel.kt:15)
    at com.example.---.viewModel.LoginViewModel$firebaseUserAuth$1.invoke(LoginViewModel.kt:12)
    at com.example.---.data.FirebaseUserAuth$login$1.onComplete(FirebaseUserAuth.kt:27)
    at com.google.android.gms.tasks.zzj.run(Unknown Source)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6077)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
2 Answers

You're using viewModel.loginAuthData.observe(this, Observer {}) in onCreateView(), i.e., using the Fragment (via this) as the LifecycleOwner for your observing. This is always wrong - you should instead use viewLifecycleOwner when observing from onCreateView():

viewModel.loginAuthData.observe(viewLifecycleOwner, Observer {
   ...
})

How LiveData works is that it continues to stay registered until the given LifecycleOwner is destroyed. When you pass this, it waits for the Fragment itself to be destroyed. However, Fragments are not destroyed while on the back stack (only their view and hence their viewLifecycleOwner is destroyed). This means that the original LiveData never unregisters its Observer.

This means that when onCreateView() happens a second time (i.e., you hit the back button and the view is recreated), a second Observer is created and registered. You now have two Observers running simultaneously. As they both run, the first one successfully runs and calls navigate(). When the second one runs, the NavController has already moved to the next destination, so any actions registered on the previous destination are no longer found, giving you an IllegalArgumentException.

By using viewLifecycleOwner, the original LiveData is correctly destroyed when the Fragment's view is destroyed. This means when you hit back and onCreateView is called again, only one Observer is active at a time and you won't run into this issue.

Related