Is this the right way to clean-up Fragment back stack when leaving a deeply nested stack?
Asked Answered
A

7

134

I'm using the Android Compatibility library to implement fragments and have extended the layout sample so that a fragment contains a button which fires off another fragment.

In the selection pane on the left I have 5 selectable items - A B C D E.

Each loads up a fragment (via FragmentTransaction:replace) in the details pane - a b c d e

Now I've extended fragment e to contain a button which loads up another fragment e1 also in the details pane. I've done this on fragment e's onClick method as follows:

FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.details_frag, newFrag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();

If I make the following selections:

E - e - e1 - D - E

Then fragment e is in the details pane. This is fine and what I want. However, if I hit the back button at this point it does nothing. I have to click it twice because e1 is still on the stack. Furthermore after clicking around I got a null pointer exception in onCreateView:

To 'solve' this problem I added the following whenever A B C D E is selected:

FragmentManager fm = getActivity().getSupportFragmentManager();
for(int i = 0; i < fm.getBackStackEntryCount(); ++i) {
    fm.popBackStack();
}

Just wondering whether this is the correct solution or whether I should be doing something different?

Azeotrope answered 27/4, 2011 at 9:45 Comment(0)
B
258

Well there are a few ways to go about this depending on the intended behavior, but this link should give you all the best solutions and not surprisingly is from Dianne Hackborn

http://groups.google.com/group/android-developers/browse_thread/thread/d2a5c203dad6ec42

Essentially you have the following options

  • Use a name for your initial back stack state and use FragmentManager.popBackStack(String name, FragmentManager.POP_BACK_STACK_INCLUSIVE).
  • Use FragmentManager.getBackStackEntryCount()/getBackStackEntryAt().getId() to retrieve the ID of the first entry on the back stack, and FragmentManager.popBackStack(int id, FragmentManager.POP_BACK_STACK_INCLUSIVE).
  • FragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) is supposed to pop the entire back stack... I think the documentation for that is just wrong. (Actually I guess it just doesn't cover the case where you pass in POP_BACK_STACK_INCLUSIVE),
Bourguiba answered 12/5, 2011 at 21:48 Comment(7)
What does this mean? FragmentManager.getBackStackEntryCount()/getBackStackEntryAt().getId()Absolutism
2nd worked for me. What it means for clearing the entire stack: getSupportFragmentManager().popBackStack(getSupportFragmentManager().getBackStackEntryAt(0).getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);Leonie
@JorgeGarcia is it possible after popbackstack we just finish our fragment without restart older one.Footlocker
Please note there is also the popBackStackImmediate() version since popBackStack() is async, meaning the clearing up does not occur at the exact moment you call the method.Aceldama
Claims the group is banned as spam and i cannot access the link :( does anyone have the resource elsewhere?Doriadorian
What if I have multiple fragments in flow like A-->B--->C-->D and from D if I want to go back to C and then to B and at last to A. But Above options always land me to A if I clicked back on B/C/D. What should be the proper way to manage this?Rosebay
@NGR, simply FragmentManager.popBackStack() at every step.Anstus
Z
62

The other clean solution if you don't want to pop all stack entries...

getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getSupportFragmentManager().beginTransaction().replace(R.id.home_activity_container, fragmentInstance).addToBackStack(null).commit();

This will clean the stack first and then load a new fragment, so at any given point you'll have only single fragment in stack

Zollverein answered 24/7, 2013 at 9:58 Comment(3)
This is very bad: getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); before a replace() because are loading the Fragment calling onCreateView(), onActivityCreated(), etc. Restoring and Destroing a Fragment instantly is bad. The Fragment can register a receiver for example. This affects the performance.Lett
@VasileM, could you describe in details? In what cases it is bad? Is it because of asynhronous behaviour of FragmentTransaction? Is there only bad performance of wrong fragment stack?Anstus
@Lett for that it could be a good practice to register observer and other jobs in OnViewCreated Fragment flow callback, or handle unregisering observers when the currently fragment is destroyed ' override fun onDestroy() { super.onDestroy() viewModel.viewState.removeObservers(this) } 'Shame
I
28

Thanks to Joachim answer, I use the code to clear all back stack entry finally.

// In your FragmentActivity use getSupprotFragmentManager() to get the FragmentManager.

// Clear all back stack.
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
for (int i = 0; i < backStackCount; i++) {

    // Get the back stack fragment id.
    int backStackId = getSupportFragmentManager().getBackStackEntryAt(i).getId();

    getSupportFragmentManager().popBackStack(backStackId, 
        FragmentManager.POP_BACK_STACK_INCLUSIVE);

} /* end of for */
Injun answered 9/11, 2012 at 7:47 Comment(4)
Why do use a loop here? For me FragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) cleared all back stack entry...Opposable
@Marek, because sometimes not all fragments should be removed, a loop is suitable for this.Anstus
@CoolMind, that still doesn't make sense to me. popBackStack(backStackId, INCLUSIVE) will pop all the fragments (states) back to backStackId, so once you pop the lowest i that you're going to pop, all the higher ones should get popped at the same time. So what's the point of a loop?Mechanician
@LarsH, you are right. See https://mcmap.net/q/67420/-is-this-the-right-way-to-clean-up-fragment-back-stack-when-leaving-a-deeply-nested-stack. In order to pop other fragments to required tag, we should first add the fragment with addToBackStack(tag).Anstus
I
7

I have researched a lot for cleaning Backstack, and finally see Transaction BackStack and its management. Here is the solution that worked best for me.

 // CLEAR BACK STACK.
    private void clearBackStack() {
        final FragmentManager fragmentManager = getSupportFragmentManager();
        while (fragmentManager.getBackStackEntryCount() != 0) {
            fragmentManager.popBackStackImmediate();
        }
    }

The above method loops over all the transactions in the backstack and removes them immediately one at a time.

Note: above code sometime not work and i face ANR because of this code,so please do not try this.

Update below method remove all fregment of that "name" from backstack.

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack("name",FragmentManager.POP_BACK_STACK_INCLUSIVE);
  • name If non-null, this is the name of a previous back state to look for; if found, all states up to that state will be popped. The
  • POP_BACK_STACK_INCLUSIVE flag can be used to control whether the named state itself is popped. If null, only the top state is popped.
Impurity answered 19/6, 2015 at 6:46 Comment(2)
I don't see why you would remove them one at a time. Is there a reason why you chose for this solution instead of the other solutions? Is there a reason to remove them one at a time instead of all at once?Capitalism
thair no method to remove backstack at one time see developer.android.com/reference/android/app/…Impurity
C
3

I'm using a similar code as those that use the while loop but I call the entry count in every loop... so I suppose it's somewhat slower

FragmentManager manager = getFragmentManager();
while (manager.getBackStackEntryCount() > 0){
        manager.popBackStackImmediate();
    }
Contradance answered 29/2, 2016 at 16:17 Comment(0)
A
0

As written in How to pop fragment off backstack and by LarsH here, we can pop several fragments from top down to specifical tag (together with the tagged fragment) using this method:

fragmentManager?.popBackStack ("frag", FragmentManager.POP_BACK_STACK_INCLUSIVE);

Substitute "frag" with your fragment's tag. Remember that first we should add the fragment to backstack with:

fragmentTransaction.addToBackStack("frag")

If we add fragments with addToBackStack(null), we won't pop fragments that way.

Anstus answered 3/12, 2019 at 13:29 Comment(0)
D
-4
    // pop back stack all the way
    final FragmentManager fm = getSherlockActivity().getSupportFragmentManager();
    int entryCount = fm.getBackStackEntryCount(); 
    while (entryCount-- > 0) {
        fm.popBackStack();
    }
Donor answered 5/3, 2013 at 21:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.