Reputation: 1249
I have a BottomNavigationView with 5 items. Sometimes I got error android.os.TransactionTooLargeException.
I used TooLargeTool to find a problem and found out that it happens when app goes background if I click on tabs many time because every time a new fragment created and finally data parcel size becomes more than 1 mb which generates an exception.
val navController = findNavController(R.id.nav_host_fragment_activity_main)
WindowInsetsControllerCompat(window,window.decorView).isAppearanceLightStatusBars = false
navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id){
R.id.rifleChoiceFragment -> {
navView.visibility = View.GONE
window.statusBarColor = defaultColor
WindowInsetsControllerCompat(window,window.decorView).isAppearanceLightStatusBars = true
}
R.id.ammoChoiceFragment -> {
WindowInsetsControllerCompat(window,window.decorView).isAppearanceLightStatusBars = true
navView.visibility = View.GONE
window.statusBarColor = defaultColor
}
R.id.dataBaseFragment-> {
navView.visibility = View.GONE
window.statusBarColor = defaultColor
WindowInsetsControllerCompat(window,window.decorView).isAppearanceLightStatusBars = true
}
R.id.caliberChoiceFragment-> {
navView.visibility = View.GONE
window.statusBarColor = defaultColor
WindowInsetsControllerCompat(window,window.decorView).isAppearanceLightStatusBars = true
}
R.id.vendorChoiceFragment-> {
navView.visibility = View.GONE
window.statusBarColor = defaultColor
WindowInsetsControllerCompat(window,window.decorView).isAppearanceLightStatusBars = true
}
R.id.secondAmmoChoiceFragment-> {
navView.visibility = View.GONE
window.statusBarColor = defaultColor
WindowInsetsControllerCompat(window,window.decorView).isAppearanceLightStatusBars = true
}
R.id.payWallFragment -> {
navView.visibility = View.GONE
window.statusBarColor = blackColor
WindowInsetsControllerCompat(window,window.decorView).isAppearanceLightStatusBars = false
}
R.id.splashFragment-> {
navView.visibility = View.GONE
window.statusBarColor = blackColor
WindowInsetsControllerCompat(window,window.decorView).isAppearanceLightStatusBars = false
}
else -> {
WindowInsetsControllerCompat(window,window.decorView).isAppearanceLightStatusBars = true
navView.visibility = View.VISIBLE
window.statusBarColor = defaultColor
}
}
}
navView.setupWithNavController(navController)
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/mobile_navigation"
app:startDestination="@+id/splashFragment">
<fragment
android:id="@+id/navigation_home"
android:name="NAME"
android:label="@string/title_home"
tools:layout="@layout/fragment_ammo" >
<action
android:id="@+id/action_navigation_home_to_rifleChoiceFragment"
app:destination="@id/rifleChoiceFragment"
app:launchSingleTop="false" />
<action
android:id="@+id/action_navigation_home_to_ammoChoiceFragment"
app:destination="@id/ammoChoiceFragment"
app:launchSingleTop="false" />
</fragment>
<fragment
android:id="@+id/navigation_dashboard"
android:name="NAME"
android:label="@string/title_dashboard"
tools:layout="@layout/fragment_table" />
<fragment
android:id="@+id/navigation_notifications"
android:name="NAME"
android:label="@string/Trajectory"
tools:layout="@layout/fragment_plot" >
<action
android:id="@+id/action_navigation_notifications_to_secondAmmoChoiceFragment"
app:destination="@id/secondAmmoChoiceFragment"
app:launchSingleTop="false" />
</fragment>
<fragment
android:id="@+id/navigation_target"
android:name="NAME"
android:label="@string/reticle"
tools:layout="@layout/fragment_target" >
<action
android:id="@+id/action_navigation_target_to_reticleChoiceFragment"
app:destination="@id/reticleChoiceFragment"
app:launchSingleTop="false" />
<action
android:id="@+id/action_navigation_target_to_targetChoiceFragment"
app:destination="@id/targetChoiceFragment"
app:launchSingleTop="false" />
<action
android:id="@+id/action_navigation_target_to_payWallFragment"
app:destination="@id/payWallFragment"
app:launchSingleTop="true" />
</fragment>
<fragment
android:id="@+id/navigation_settings"
android:name="NAME t"
android:label="@string/settings"
tools:layout="@layout/settings_fragment" />
<fragment
android:id="@+id/rifleChoiceFragment"
android:name="NAME"
android:label="rifle_choice_fragment"
tools:layout="@layout/rifle_choice_fragment" />
<fragment
android:id="@+id/ammoChoiceFragment"
android:name="NAME"
android:label="fragment_ammo_choice"
tools:layout="@layout/fragment_ammo_choice" >
<action
android:id="@+id/action_ammoChoiceFragment_to_dataBaseFragment"
app:destination="@id/dataBaseFragment"
app:launchSingleTop="false" />
</fragment>
<fragment
android:id="@+id/dataBaseFragment"
android:name="NAME"
android:label="fragment_data_base"
tools:layout="@layout/fragment_data_base" >
<action
android:id="@+id/action_dataBaseFragment_to_caliberChoiceFragment"
app:destination="@id/caliberChoiceFragment"
app:launchSingleTop="false" />
<action
android:id="@+id/action_dataBaseFragment_to_vendorChoiceFragment"
app:destination="@id/vendorChoiceFragment"
app:launchSingleTop="false" />
</fragment>
<fragment
android:id="@+id/caliberChoiceFragment"
android:name="NAME"
android:label="fragment_caliber_choice"
tools:layout="@layout/fragment_caliber_choice" />
<fragment
android:id="@+id/vendorChoiceFragment"
android:name="NAME"
android:label="fragment_vendor_choice"
tools:layout="@layout/fragment_vendor_choice" />
<fragment
android:id="@+id/secondAmmoChoiceFragment"
android:name="NAME"
android:label="fragment_second_ammo_choice"
tools:layout="@layout/fragment_second_ammo_choice" >
<action
android:id="@+id/action_secondAmmoChoiceFragment_to_dataBaseFragment"
app:destination="@id/dataBaseFragment" />
</fragment>
<fragment
android:id="@+id/reticleChoiceFragment"
android:name="NAME"
android:label="fragment_reticle_choice"
tools:layout="@layout/fragment_reticle_choice" />
<fragment
android:id="@+id/targetChoiceFragment"
android:name="NAME"
android:label="fragment_target_choice"
tools:layout="@layout/fragment_target_choice" />
<fragment
android:id="@+id/payWallFragment"
android:name="NAME"
android:label="fragment_pay_wall"
tools:layout="@layout/fragment_pay_wall" >
<action
android:id="@+id/action_payWallFragment_to_navigation_home"
app:destination="@id/navigation_home"
app:launchSingleTop="false"
app:popUpTo="@id/mobile_navigation"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/splashFragment"
android:name="NAME"
android:label="fragment_splash"
tools:layout="@layout/fragment_splash" >
<action
android:id="@+id/action_splashFragment_to_navigation_home"
app:destination="@id/navigation_home"
app:launchSingleTop="false"
app:popUpTo="@id/mobile_navigation"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_splashFragment_to_payWallFragment"
app:destination="@id/payWallFragment"
app:launchSingleTop="false"
app:popUpTo="@id/mobile_navigation"
app:popUpToInclusive="true" />
</fragment>
</navigation>
bottom nav menu:
<?xml version="1.0" encoding="utf-8"?>
<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_bulletsvg"
android:title="@string/title_home" />
<item
android:id="@+id/navigation_dashboard"
android:icon="@drawable/ic_spreadsheet"
android:title="@string/title_dashboard"
/>
<item
android:id="@+id/navigation_notifications"
android:icon="@drawable/ic_b"
android:title="@string/Trajectory"
/>
<item
android:id="@+id/navigation_targ"
android:icon="@drawable/ic_ret"
android:title="@string/ret"
/>
<item
android:id="@+id/navigation_settings"
android:icon="@drawable/ic_set"
android:title="@string/settings"
/>
What's the proper way to fix it? Should I handle click on each tab manually?
Upvotes: 0
Views: 330
Reputation: 199825
This is your problem:
app:startDestination="@+id/splashFragment"
As per the Principles of Navigation, you should be using your actual home screen as the start destination of your graph:
app:startDestination="@+id/navigation_home"
You should never be using a splash destination as your start destination - as per those exact same principles:
Note: An app might have a one-time setup or series of login screens. These conditional screens should not be considered start destinations because users see these screens only in certain cases.
This is how you should be implementing your paywall screen - by following the Conditional Navigation documentation to conditionally navigate from your actual start destination to the paywall screen.
Why is this the source of the problem with your bottom navigation? It is because setupByNavController
pops up to the start destination of your app when swapping between tabs, thus ensuring that each tab is independent from one another. When the start destination of your app isn't on the back stack, there is no single point of reference in the back stack to know what to pop up to so what happens is that nothing gets popped at all - you just keep building up more and more and more of a back stack instead of replacing one back stack with another as you swap tabs. By keeping the start destination of your app on the back stack, setupWithNavController
, which assumes you are following the Principles of Navigation, will work as intended.
You'll find that using a splash screen for your start destination also breaks any deep links you add to your graph as it is the start destination that is added to the back stack and you want your home screen to be on the back stack, not your splash screen.
Upvotes: 1