简体   繁体   中英

NavController crashes upon orientation change

I'm not really sure what I'm doing wrong here. Simple setup, single activity with fragments controlled by a bottom bar inside, so far so good. The start fragment has a button inside which should navigate to another fragment.

Here's the onclicklistener for the button inside my fragment class:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    add_step_fab.setOnClickListener {
        Navigation.findNavController(view).navigate(R.id.fragmentAtoB)
    }
}

Here's my action in the navigation.xml:

<fragment android:id="@+id/navigation_main" android:name="com.test.test.fragments.AFragment"
          android:label="Fragment A" tools:layout="@layout/fragment_a">
    <action android:id="@+id/fragmentAtoB"
            app:destination="@id/fragmentB" app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
            app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
</fragment>

The navigation works, but I get the standard

java.lang.IllegalArgumentException: navigation destination com.test.test:id/fragmentAtoB is unknown to this NavController

error when I click on the button after rotating my screen, split screening the app, or sometimes seemingly randomly. Looks like it has something to do with the configuration change?

EDIT: I also tried using

val clickListener: View.OnClickListener = Navigation.createNavigateOnClickListener(R.id.fragmentAtoB)
    add_step_fab.setOnClickListener(clickListener)

with the same issue as above.

Checking if i'm actually in the same fragment, as some suggested for users who are having the same exception (but for unrelated reasons), such as:

if (controller.currentDestination?.id == R.id.navigation_main) {
            controller.navigate(R.id.fragmentAtoB)
        }

also didn't help.

I also experienced this recently after months of an app working correctly. I originally had added navigation to an app that was utilizing a "MainActivity" class and then had other activities that would be launched from a menu (as opposed to having the Navigation component navigate between fragments off of a single Activity).

So the app is organized such that the MainActivity is the "hub", and choices from menu options cause other activities to be launched, and when the user returns from them they are back at the MainActivity - so a pure "hub (MainActivity) and spoke (5 other activities)" model.

Within MainActivity.onCreate() is called a setupNavigation() method which originally looked like:

private void setupNavigation() {
    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    drawerLayout = findViewById(R.id.drawer_layout);

    // This class implements the listener interface for the NavigationView, handler is below.
    NavigationView navigationView = findViewById(R.id.nav_view);
    navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);
    NavigationUI.setupWithNavController(navigationView, navController);

    // MainActivity implements the listener for navigation events
    navigationView.setNavigationItemSelectedListener(this);

}

As I mentioned, this was working just fine even with configuration changes until recently. Even just changing the configuration from portrait to landscape on the MainActivity would cause an exception:

ComponentInfo{com.reddragon.intouch/com.reddragon.intouch.ui.MainActivity}: java.lang.IllegalStateException: You must call setGraph() before calling getGraph()

I found I had to add this line:

navController.setGraph(R.navigation.nav_host);

Right after the Naviation.findNavController() call, and then it handled configuration changes just fine.

After a configuration change, the fragment or activity goes out of scope temporarily and then executes an onResume() override. If you have anything which is initialized in onCreate(), or onViewCreated(), then those variables may have gone out of scope and are now null values. So, I usually have a method called RestoreAll() which is responsible for instantiating all of the objects used by the code, and I call this method from onResume() so that every time there is a configuration change, all the local objects are re-instantiated.

There is a problem with nav controller backstack after changed config. Try this: https://stackoverflow.com/a/59987336/5433148

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM