Hey, I have started rolling out a new version of m...
# compose-wear
t
Hey, I have started rolling out a new version of my Wear app using Compose Wear to some beta testers. Some users are getting the following crash:
Copy code
Fatal Exception: java.lang.IllegalArgumentException: No WearNavigation.Destination has been added to the WearNavigator in this NavGraph. For convenience, build NavGraph using androidx.wear.compose.navigation.composable.
       at androidx.wear.compose.navigation.SwipeDismissableNavHostKt.SwipeDismissableNavHost(SwipeDismissableNavHostKt.java:166)
       at androidx.wear.compose.navigation.SwipeDismissableNavHostKt.SwipeDismissableNavHost(SwipeDismissableNavHostKt.java:87)
       ... (redacted)
Note that it works great for most users, so far this crash is only affecting a few users. I am not able to reproduce it myself. I am creating my nav host like this:
Copy code
SwipeDismissableNavHost(
    navController = navController,
    startDestination = "mydestname"
) {
    // Note that composable is from androidx.wear.compose.navigation package.
    composable("mydestname") {
        ...
    }
    ... more composables
}
The crash seems unexpected to me as there are always destinations added, so I suspect this is a bug. This is using androidx.wear.compose:* snapshot 8104636 and androidx.navigation:* 2.4.0-rc01. Any suggestions?
This is different behaviour than the standard
NavHost
from navigation, as that does not throw an exception but just shows nothing instead: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigati[…]NavHost.kt;l=139;drc=78d498661680a95d2705d5e6dbb1c0438c50fa30
I am not sure in what state of the app this could be the case, but maybe
SwipeDismissableNavHost
should be changed to match the behaviour of
NavHost
? So do not throw an exception if backstack is empty, but just show nothing.
j
@Steve Bower [G]
t
From what I can see in Firebase Crashlytics, is that the crash seems to be occurring within a few seconds after the app is moved to foreground. (I think)
Also seems the same 2 users got the crash multiple times (12):
These 2 users have a Samsung watch with Android 11 and a Mobvoi watch with Android 9.
Google Play reported 3 crashes for 2 users. Maybe Google Play just not reports all the crashes.
s
Hi Thomas - this is very odd, that error was added to highlight issues where the wrong 'NavGraphBuilder.composable' is called when building the graph (because if the androidx.wear.compose.navigation.composable is not used, the WearNavigator will not have any destinations). This is one of the reasons for only depending on wear.compose.navigation and not including the navigation.compose library (their composable is https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigati[…]main/java/androidx/navigation/compose/NavGraphBuilder.kt;l=35).
For context, this error was added in response to feedback that it was possible to depend on the wrong NavGraphBuilder.composable, in which case the app would only show a blank screen, which was hard to debug. See https://android-review.googlesource.com/c/platform/frameworks/support/+/1853516
l
Could some gradle metadata be added to make these two navigation libraries mutually exclusive, and also ensure that no one can use just the mobile/tablets dedicated navigation library on a watch?
t
Thanks for looking into it Steve. I understand why the error is there, but it seems that, even when using the correct composable function, the destinations are empty (sometimes?). I am not that familiar with the navigation internals so I don’t know why this happens.
It also seems that the users who got the crash got it multiple times, as Firebase shows:
This issue has *22 crash events* affecting *5 users*
s
I've been digging into this a bit - it does seem that the destinations have been registered with the NavGraphBuilder, and the start destination is present, otherwise we'd see error "navigation destination <name> is not a direct child of this NavGraph". But there's nothing in the nav back stack for the Wear Navigator, which either means the destinations are registered with the wrong navigator type (which you've checked, according to the code comment) or somehow the back stack has not been populated at the time that it's queried. I'll carry on looking at this tomorrow. It would be great to know if the users experiencing the crash see anything on the app or if it always crashes immediately - that would suggest we need to find out why the backstack is sometimes empty and be defensive when querying it.
t
Yes, the composable function is indeed from the correct package. The app works fine for me and also most other testers. Unfortunately, I do not have any more details at the moment. There are 5 users who got the crash on Firebase but none of them have reached out to me. So I do not know what they see in the app.
I have been trying to reproduce the crash myself (on a Samsung Galaxy Watch4 just like some crashes reported), but no luck so far.
message has been deleted
I know that without any more details it is hard for you to find the issue. If there is any more information you need, please let me know. Is there anything I could be doing wrong somewhere else in my code which could be causing this?
I've also started rolling out a new build with a newer wear compose snapshot so in a few days I hope to be able to let you know if that makes a difference.
s
Hi Thomas - I've spent this morning trying to reproduce the error, for example by deliberately using the wrong destination name or the wrong navcontroller (rememberNavController instead of rememberSwipeDismissableNavHost) - but all of these cases would generate different errors. That was working from the assumption that the error occurs when the app starts up, but given that your app uses the correct 'composable', I don't think there's a way to trigger the error at startup. I am wondering if the app manipulates the backstack directly at all? For example, a simple way to reproduce the issue is by calling navController.popBackStack() at any point on the top level screen - that leaves an empty backstack and triggers the error. Note that, at the top level in the hierarchy, SwipeDismissableNavHost leaves handling the swipe gesture to the system swipe-to-dismiss - that way, a swipe neatly exits the app. So there is no need for the app to pop the stack to exit. If this is why the exception is triggered, we can update the docs and improve the error message.
t
Hi Steve, thanks for the detailed analysis. I do use popBackStack in some places so I am looking into it now if this could be the issue. I already tested it and it seems that popping the root destination does indeed cause this error. I am looking through my code now if this could be the case. A better error message for this would definitely be more helpful.
Okay so the only piece of code which I think could be causing the issue is the following:
Copy code
navController.popBackStack(Screen.Intro.route, true)
if (navController.backQueue.isEmpty()) {
    navController.navigate(Screen.Groups.route)
}
What I am trying to do is navigate to another screen (groups) by popping the screen that is currently visible (intro). Either the groups or intro screen could be currently the root destination. I have rewritten the code above to use popBackStack or use navigate/popUpTo (depending on which screen is the root). Will push a new update to see if this fixes the crash, that could take a few days for Google to review it. Thanks a lot for your help so far looking into this, I really appreciate it.
s
No problem - it does sound like that was the issue. Assuming no more crashes, we'll update the error message accordingly.
t
Hi Steve, the update was approved by Google but unfortunately the crashes started appearing again. I guess the app may be calling popBackStack somewhere incorrectly, but I am not sure. I do use popBackStack a lot. I will have to do some more debugging on this issue to find out where this is going wrong.
s
That's a shame - I've improved the error message anyway. Please do let us know if you find out any more.