I couldn’t catch this problem personally, but there are a lot of such errors on Firebase Crashlytics
I tried all the answers with StackOverflow but nothing helped
My R.layout.activity_main
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/main_graph"/>
My navigation/main_graph
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tols"
xmlns:tools2="http://schemas.android.com/tools"
android:id="#+id/main_graph"
app:startDestination="#id/ordersFragment">
<fragment
android:id="#+id/ordersFragment"
android:name="com.app.package.ui.orders.view.OrdersFragment"
android:label="#string/menu_orders"
tools:layout="#layout/fragment_orders">
<action
android:id="#+id/action_ordersFragment_to_orderFragment"
app:destination="#id/orderFragment" />
<action
android:id="#+id/action_ordersFragment_to_problemFragment"
app:destination="#id/problemFragment" />
<action
android:id="#+id/action_ordersFragment_to_settingsFragment"
app:destination="#id/settingsFragment" />
</fragment>
<fragment
android:id="#+id/problemFragment"
android:name="com.app.package.ui.problem.ProblemFragment"
android:label="#string/menu_problems" />
<fragment
android:id="#+id/orderFragment"
android:name="com.app.package.ui.order.view.OrderFragment"
android:label="OrderFragment">
<action
android:id="#+id/action_orderFragment_to_compositionFragment"
app:destination="#id/compositionFragment" />
<action
android:id="#+id/action_orderFragment_to_selectCheckDialog"
app:destination="#id/selectCheckDialog" />
</fragment>
<fragment
android:id="#+id/compositionFragment"
android:name="com.app.package.ui.composition.CompositionFragment"
android:label="fragment_composition"
tools2:layout="#layout/fragment_composition" />
<fragment
android:id="#+id/settingsFragment"
android:name="com.app.package.ui.settings.SettingsFragment"
android:label="fragment_settings"
tools2:layout="#layout/fragment_settings" />
<fragment
android:id="#+id/selectCheckDialog"
android:name="com.app.package.ui.order.view.SelectCheckDialog"
android:label="dialog_select_check"
tools2:layout="#layout/dialog_select_check" />
</navigation>
My gradle
implementation 'androidx.navigation:navigation-fragment:2.2.0'
implementation 'androidx.navigation:navigation-ui:2.2.0'
I tried different solutions but nothing helped.
Judging by Firebase Crashlytics 963 crash in 342 users.
Encountered this when tried to rotate the phone, while using the application.
This happened to me because I was using Fragment transactions in onCreate, even though nav_graph.xml handles that for us.
I just changed:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow()
}
}
to:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
}
I had the same issue.
First of all, what is the source of this issue?
If the app remains in the background for a while android will destroy the activities that are in the back stack. So here are some possible steps:
User opens the app
Navigate to another activity from the MainActivity
Put the app in the background
Come back after a while
Navigate back to MainActivity
CRASH
You can reproduce it easier if you check the "Don't keep activities" option
in "Developer Option"
Possible fix: Replace fragment tag from activity_main with FragmentContainerViewbut at least for me this generates other issues.
I got this error in a new template-created project because I added the host fragment inside a DrawerLayout without thinking about the include for the app bar which has an include for content where there's another host fragment. It was a facepalm moment. I was copying pieces of code from an existing project to speed things up and went a bit too far there.
Removing either host fragment fixed the crash. Changing the id of one of them also fixed the crash, but then the nav graph was inflated twice. Of course, I wanted to use the one in the app bar content instead of the one I added.
It looks like your activity_main layout is much simpler if that's the whole thing, but check to make sure it's not possible for a layout to include more than one host fragment with the same id.
Related
I have been using the Jetpack Navigation component (2.3.5, although I have tested older versions too) and trying to add transitions between two simple destinations. However, the transition specified with the popEnterAnim attribute doesn't run at all. Instead the re-entering fragment just immediately appears.
I have managed to recreate it with a really simple example. The fragment layouts are just a single TextView.
Main activity
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
class ActivityMain : AppCompatActivity(R.layout.main)
Fragment 1
class Fragment1 : Fragment(R.layout.fragment_1) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.setOnClickListener {
findNavController().navigate(R.id.action_fragment1_to_fragment2)
}
}
}
Fragment 2
class Fragment2: Fragment(R.layout.fragment_2)
Navigation graph
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:id="#+id/nav_graph.xml"
app:startDestination="#id/fragment1">
<fragment
android:id="#+id/fragment1"
android:name="uk.henrytwist.projectsource.Fragment1"
android:label="MainFragment" >
<action
android:id="#+id/action_fragment1_to_fragment2"
app:destination="#id/fragment2"
app:enterAnim="#anim/transition_rise_up_enter"
app:exitAnim="#anim/transition_fade_exit"
app:popEnterAnim="#anim/transition_fade_pop_enter"
app:popExitAnim="#anim/transition_rise_up_pop_exit" />
</fragment>
<fragment
android:id="#+id/fragment2"
android:name="uk.henrytwist.projectsource.Fragment2"
android:label="Fragment2" />
</navigation>
It turns out that this was actually due to a bug in the AndroidX fragment library (androidx.fragment:fragment:1.3.2) which has now been rectified in version 1.3.3. The changelog confirms the fix:
Fixed a regression introduced in Fragment 1.3.2 which would cause popEnter animations to not run when popping a FragmentTransaction that included a setPrimaryNavFragment operation, such as those used by NavHostFragment. (I38c87, b/183877426)
I have an app similar to Android Studios Navigation Drawer Activity:
My activity uses Android Architecture Navigation Components & a navigation drawer to navigate between different fragments.
As the navigation drawer is pretty custom i can't use the usual navigation-view, but use a custom fragment hosting a LinearLayout.
Each item in that LinearLayout has an onClickListener which boils down to
navController.navigate(R.id.myCorrespondingFragment)
So far, so everything works fine.
The problem begins when navigating back:
Let's imagine i navigate from "Home"-Fragment A -> B -> C and then go back.
Android Studios example behaves correctly: C -> A -> close
My implementation doesn't: it just pops the backstack C -> B -> A -> close
How do i fix that?
Minified Main-Layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout android:id="#+id/drawerLayout" >
<LinearLayout android:orientation="vertical" >
<androidx.appcompat.widget.Toolbar android:id="#+id/toolbar" />
<fragment
android:id="#+id/container"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph"
/>
</LinearLayout>
<fragment
android:name="com.company.drawer.DrawerFragment"
android:layout_gravity="start"
/>
</androidx.drawerlayout.widget.DrawerLayout>
Navigation graph:
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/navigation_graph"
app:startDestination="#id/doorFragment"
>
<fragment
android:id="#+id/doorFragment"
android:name="com.company.door.DoorFragment"
android:label="#string/shared_empty"
/>
<fragment
android:id="#+id/historyFragment"
android:name="com.company.history.HistoryFragment"
android:label="#string/history"
/>
<fragment
android:id="#+id/settingsFragment"
android:name="com.company.settings.SettingsFragment"
android:label="#string/settings"
/>
</navigation>
Everything navigation related from my Main Activity's onCreate:
setSupportActionBar(toolbar)
val navController = (supportFragmentManager.findFragmentById(R.id.container) as NavHostFragment).navController
toolbar.setupWithNavController(navController, AppBarConfiguration(
listOf(R.id.doorFragment, R.id.historyFragment, R.id.settingsFragment)
, drawerLayout
))
You can try like this
<fragment
android:id="#+id/fragmentA"
android:name="com.package.FragmentA"
android:label="Fragment A">
<action
android:id="#+id/action_fragmentA_to_fragmentB"
app:destination="#id/fragmentB"
app:popUpTo="#id/fragmentB"
app:popUpToInclusive="true / false" />
</fragment>
You can use global actions, so that you don't have to add an action for every fragment to fragment navigation but only for every fragment destination. This would look like this in XML:
<action android:id="#+id/action_global_doorFragment"
app:destination="#id/doorFragment"
app:popUpTo="#id/#id/doorFragment"
app:restoreState="true"
app:popUpToSaveState="true" />
<action android:id="#+id/action_global_historyFragment"
app:destination="#id/historyFragment"
app:popUpTo="#id/#id/doorFragment"
app:restoreState="true"
app:popUpToSaveState="true" />
<action android:id="#+id/action_global_settingsFragment"
app:destination="#id/historyFragment"
app:popUpTo="#id/#id/doorFragment"
app:restoreState="true"
app:popUpToSaveState="true" />
Note that I always used your start destination for popUpTo and also save the backstack state. You don't necessarily need the latter (restoreState and popUpToSaveState) for your use case but maybe want it later down the road when you have multiple fragments for a single drawer entry. This will get you the same behaviour as the current standard drawer integration.
Here's an easy example on how you can use the global action to go to your settings fragment. (The docs also explain how you can use global actions with the SafeArgs Gradle Plugin which is probably the better method.)
navController.navigate(R.id.action_global_settingsFragment)
If you don't want an extra action for every destination and your drawer menu items have the same id as its destination, you can also do it programmatically like this:
override fun onNavigationItemSelected(item: MenuItem): Boolean {
navController.navigate(item.itemId, null, navOptions {
restoreState = true
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
})
return true
}
I use navigation components to navigate from one fragment to another. However, when the user press the back button, I want to navigate back to first fragment. But it keep showing the second fragment. This is my nav_graph:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/fragment1">
<fragment
android:id="#+id/fragment2"
android:name="com.myapp.ui.fragments.Fragment2"
android:label="fragment_2" />
<fragment
android:id="#+id/fragment1"
android:name="com.myapp.ui.fragments.Fragment1"
android:label="fragment_1">
<action
android:id="#+id/action_fragment1_to_fragment2"
app:destination="#id/fragment2"
app:enterAnim="#anim/fragment_fade_enter"
app:exitAnim="#anim/fragment_fade_exit"
app:popUpTo="#id/fragment1" />
</fragment>
</navigation>
And this is how I trigger the navigation in the code of my Fragment1-Class:
viewModel.itemSelected.observe(viewLifecycleOwner) {
navigate(it)
}
....
fun navigate(id: Long){
val bundle = Bundle()
bundle.putLong("itemid", id)
getNavController().navigate(R.id.action_fragment1_to_fragment2, bundle)
}
Edit:
Corrected startDestination in XML.
Edit2:
Added more code.
You're using a LiveData for an event. LiveData always caches the set value, so when you return to your Fragment1, you observe the LiveData again and get the same value a second time, causing you to navigate() yet again.
See this blog post for more information and alternatives.
I started using navigation component in my application and I am facing with the following problem.
My first fragment is LoginFragment. After a success login, the mainFragment is displayed. I want that when user is on mainFragment and press back button to not go back to loginFragment. For this I added these 2 lines in nav_graph : app:popUpTo="#+id/lovable_app_navigation" and app:popUpToInclusive="true" and it works well. Here is my navigation graph :
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/app_navigation"
app:startDestination="#id/loginFragment">
<fragment
android:id="#+id/loginFragment"
android:name="com.xxx.LoginFragment"
android:label="LoginFragment"
tools:layout="#layout/login_fragment">
<action
android:id="#+id/dashboard_action"
app:destination="#id/mainFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/app_navigation"
app:popUpToInclusive="true"/>
</fragment>
<fragment
android:id="#+id/mainFragment"
android:name="com.xxx.MainFragment"
android:label="MainFragment"
tools:layout="#layout/main_fragment">
<action
android:id="#+id/logout_action"
app:destination="#id/loginFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/app_navigation"
app:popUpToInclusive="true"/>
</fragment>
<action
android:id="#+id/action_global_loginFragment"
app:destination="#id/loginFragment" />
</navigation>
The problem is that after a time, when my session expires, it doesn't matter where the user is in application, in which fragment, I must to display the LoginFragment over the all stack. I created a global action for this action_global_loginFragment. The problem is that when I navigate to LoginFragment I get this error :
java.lang.IllegalStateException: Fragment LoginFragment{1d6bd24 (829a6832-3480-4bcb-a3f6-7e2ba214d3ca)} not associated with a fragment manager.
If I remove popUpTo and popUpToInclusive it works fine, but then the back button functionality is affected, from mainFragment it goes back to loginFragment.
Any idea how to fixed this?
Thanks in advance.
The same issue was happening to me, I managed to solve it by wrapping the navigate method inside a view?.post call like so:
view?.post {
findNavController().navigate(SplashFragmentDirections.actionSplashFragmentToLoginFragment())
}
The problem is caused when you try to call findNavController() on a fragment that has been detached, if you're using Kotlin you can create an extension function like so
fun Fragment.findNavControllerSafely(): NavController? {
return if (isAdded) {
findNavController()
} else {
null
}
}
then use it in any fragment
findNavControllerSafely()?.navigate(/*pass here the nav directions>*/)
you can also surround it with try/catch but I do not recommend this as it will silently catch/ignore other exceptions that might be useful, try navigating through the source code of findNavController() to get a better feel of the kind of exceptions that are thrown
for me the problem was the fragment after restoration because of config change was the fragment manager was not valid in fragment so I had to get it directly from activities like this:
activity?.findNavController(R.id.nav_host_fragment)?.navigate(....)
or
activity?.supportFragmentManager?.setFragmentResultListener(.....)
I was facing the same issue today.
the problem was with the importing of the wrong file.
there are actually 3 files that can be imported.
import androidx.navigation.fragment.findNavController
This says findNavController()
import androidx.navigation.Navigation.findNavController
This says findNavController(Activity, Int)
import androidx.navigation.fragment.NavHostFragment.findNavController
This says findNavController(Fragment)
If I used 2nd one it gave an error Fragment not attached/associated with/to an Activity. (don't remember the exact error)
If I used 3rd one I was getting the same error as #Gabrielle
so, for me, the first one worked perfectly fine since it doesn't require either Activity or Fragment.
I've had this issue too and its very confusing to know whats wrong!
I found this URL That helped me a lot!:Android Navigation Component Issue
This guy suggest to create a function on every fragment, due to when using the findNavController() with a fragment that isn't NavHostFragment or isn't within NavHostFragment this exception will be thrown.
fun Fragment.getFragmentNavController(#IdRes id: Int) = activity?.let {
return#let Navigation.findNavController(it, id)
}
Then, you can all your navigation instance with the id just like this:
getFragmentNavController(R.id.shipping_host_nav).navigate(R.id.id_of_host_nav)
im cereate an extention for this issue , it work based on fragments lifecycle and kotlin
fun NavController.lifeCycleNavigate(lifecycle :LifecycleCoroutineScope, resId :Int) =
lifecycle.launchWhenResumed {
navigate(resId)
}
now we can use it simply with navController , like this
findNavController().lifeCycleNavigate(lifecycleScope , R.id.destination )
I am trying out the new Navigation Architecture Component, and I can't figure out how to do this:
I have 1 Activity (MainActivity) + 3 Fragments:
SplashFragment (Home)
MainFragment
SignUpFragment
I would like to use SplashFragment to determine if I should navigate to MainFragment or SignUpFragment, but once it reaches either of those 2, you should not be able to pop back to SplashFragment. How can I do that with the new navigation component?
I tried popBackStack before and after calling navigate(R.id.action_xxx), but neither of them work (which make sense: before it has nothing to pop; after it just closes the fragment that just got added). Does that mean the only way to do that is to override onBackPress to intercept it and make sure navigateUp does not get call in those cases?
Thanks!
First, add attributes app:popUpTo='your_nav_graph_id' and app:popUpToInclusive="true" to the action tag.
<fragment
android:id="#+id/signInFragment"
android:name="com.glee.incog2.android.fragment.SignInFragment"
android:label="fragment_sign_in"
tools:layout="#layout/fragment_sign_in" >
<action
android:id="#+id/action_signInFragment_to_usersFragment"
app:destination="#id/usersFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
Second, navigate to the destination, using the above action as parameter.
findNavController(fragment).navigate(SignInFragmentDirections.actionSignInFragmentToUserNameFragment())
NOTE: If you navigate using method navigate(#IdRes int resId), you won't get the desired result. Hence, I used method navigate(#NonNull NavDirections directions).
This worked for me in alpha05 release. Add app:popUpTo="#id/nav_graph" in the action tag(inside your nav_graph.xml file).
Here "#id/nav_graph is the id of my graph or also called as the Root.
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/startFragment">
.......
<action
android:id="#+id/action_startFragment_to_homeFragment"
app:destination="#id/homeFragment"
app:popUpTo="#id/nav_graph"/>
.......
You can also do this in design tab:- select "SplashFragment" and select the action you want to change and then pick "root" for "Pop To"
WARNING: clearTask has been deprecated and will be remove in future release, not sure what the solution is. Please follow this issue for now to keep up to date
Oh after 10 minutes finally found the key: use clearTask.
All I have to do is add app:clearTask="true" to that specific action, or use .navigate(R.id.actionXXXX, null, NavOptions.Builder().setClearTask(true).build()), and it's done. Just make sure you add it to all the children of SplashFragment (in this case, both MainFragment and SignUpFragment).
So if you have splash fragment and main fragment and you don't want to go back to splash fragment after the main fragment below method you can achieve this
<fragment
android:id="#+id/splashFragment"
android:name="com.example.youappname.views.SplashFragment"
android:label="fragment_splash"
tools:layout="#layout/fragment_splash">
<action
android:id="#+id/action_splashFragment_to_mainFragment"
app:destination="#id/mainFragment"
app:popUpTo="#id/splashFragment"
app:popUpToInclusive="true"/>
</fragment>
In you Kotlin Splash Fragment:
private lateinit var navController: NavController
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
}
private fun navigateToMainFrag() {
navController.navigate(R.id.action_splashFragment_to_mainFragment)
}
Now when you press back button it will close the app instead of showing the splash screen
For anyone wanted to do this purely in code:
Navigation.findNavController(v)
.navigate(R.id.action_splashFragment_to_userProfileFragment2, null,
new NavOptions.Builder().setPopUpTo(R.id.splashFragment, true).build())
The sample solution is add a onBackPressedDispatcher on Owner Activity of fragment/navigation:
https://developer.android.com/guide/navigation/navigation-custom-back#implement_custom_back_navigation