Managing the Fragment Back Stack
Or: A lesson in not fighting the framework
The Android framework provides simple APIs for managing your back stack without headache for most simple applications. However at some point you are likely going to run into a situation that doesn’t quite fit the mold of a an application with a simple back stack.
Take, for example, an app that uses bottom navigation. If we start on Tab 1, drill down to a sub screen, then switch to Tab 2, we may want to pop off all the subscreens on Tab 1 before switching to the second tab.
If you want to jump right to the sample code, you can find it here on GitHub.
Fighting the framework
In Advocating Against Android Fragments, Pierre-Yves Ricau teaches an important lesson- there’s nothing particularly magical about the Android framework’s back stack. You can manage the back navigation of your application however you want, with or without the framework.
This is true, and you shouldn’t be afraid to manage the back stack yourself if the framework simply isn’t going to work in your scenario. Before you do that, however, make sure you understand the framework and how it can work for you.
One solution: manage the backstack yourself
Here is one way to handle popping off a certain subset of the backstack yourself by keeping track of how many “subscreens” are on the stack:
When I used this approach, I ran into a few quirks that I didn’t like.
First and foremost, it isn’t flexible. What if you add another “level” of back stack management into your app? Then you need to keep track of that too. Going back partway through the stack isn’t particularly easy with this approach.
The other quirk I didn’t like with this approach was that when calling popBackStackImmediate() in succession, each intermediate Fragment is resumed very briefly. This resulted in some code executing for a Fragment that wasn’t visible and wasn’t going to be visible.
Working with the Framework
As it turns out, the Android framework offers a solution that works very well for this and many other situations.
When adding a Fragment to the back stack, Android developers most commonly use addToBackStack(null). Why do we always pass in null? It turns out we can pass in something else that makes the Fragment back stack much more useful.
Tags and names
Fragment transactions can involve two different types of tags. The one that most Android developers are familiar with is the Fragment tag, which you can use to find a specific Fragment in your FragmentManager later via findFragmentByTag(). This is useful for finding a Fragment when your application is in a particular state, but keep in mind that the Fragment needs to be added to your FragmentManager. If you have removed() or replaced() a Fragment and haven’t added it to the backstack, you won’t be able to find it.
The other type of tag is the BackStackRecord’s name passed in to addToBackStack(). This name identifies a particular back stack record, which is a record of what occurred in a particular transaction. popBackStackImmediate() and its counterparts have variants that accept a back stack record name to pop the back stack to a particular state.
The back stack tag solution
Here’s how to solve our problem with back stack tags. I’m actually just going to be using one tag, which identifies the current “first level” Fragment (tab) on the stack.
With this code, the back stack just manages itself! We don’t need to tell it to pop are the right time or keep track of what Fragments we have added.
Inclusive vs Exclusive
There’s one last thing to note in my example, and that’s the use of FragmentManager.POP_BACK_STACK_INCLUSIVE. This tells the FragmentManager to pop our root Fragment state along with everything else. In my example this means that the current tab also gets popped off, creating a clean slate for the new tab we are about to display.
The other option is to pass in zero, which will pop everything up to the specified state. In my example this would leave the current tab on the backstack, so hitting back after navigating to a tab would bring you back to the previous tab.