Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding fragments backstack

So, there are some similar questions, but I have not found a single one solving this issue. The official android documentation seems intuitive but when I implement an app with a more complicated workflow, the fragments backstack gets messy and weird stuff starts happening. I developed a skeleton for simple apps, with the idea of a single activity which can be accessed by its fragments to start other fragments. This is how I did it:

1- I let my main activity implement an interface called "FragmentDelegate"

public interface FragmentDelegate {
    public void startFragment(CCFragment fragment, boolean addToBackStack);
}

2- The implementation of the startFrargment method:

@Override
public void startFragment(CCFragment fragment, boolean addToBackStack) {

    FragmentManager fragmentManager = getSupportFragmentManager();

    fragment.setFragmentDelegate(this);
    FragmentTransaction fragmentTransaction = fragmentManager
            .beginTransaction();

    fragmentTransaction.setCustomAnimations(R.anim.slide_in_right,
            R.anim.slide_out_left, R.anim.slide_in_left,
            R.anim.slide_out_right);

    fragmentTransaction.replace(CONTENT_VIEW_ID, fragment,
            "callerFragmentClassName");
    if (addToBackStack)
        fragmentTransaction.addToBackStack("callerFragmentClassName");
    fragmentTransaction.commitAllowingStateLoss();

}

The cool thing about this is, I can call from any fragment:

mFragmentDelegate.startFragment(aNewFragment, addToBackStack);

OK now think of the following case scenario:

I start my activity with an initial fragment, lets say fragment A. From fragment A, I call the Camera Activity for a result. When the result arrives, I start Fragment B (adding A to the backstack). From B I start Fragment C without adding B to the backstack. So we have this in the backstack:

[A] [C]

If I press the back button, I get back to A. If I repeat the process, the backstack gets messed up and when I press back, it takes me to the C fragment again and again...

I know this is difficult to understand (and more difficult for me to explain, because English is not my mother tongue) but if someone could explain to me how do the android fragments backstack really work, or provide some kind of skeleton for an app, would be great.

like image 487
Sebastian Breit Avatar asked Apr 21 '14 14:04

Sebastian Breit


2 Answers

Explanation:

When the FragmentManager reverts saved FragmentTransactions (e.g. the user click the Back button or you called some pop method on it), it executes the opposite of the action that's stored in the saved FragmentTransaction. You can see the exact code here, it's pretty straightforward.

In your case, the saved operation type is 'replace', so reverting that means removing all added Fragment instances and re-adding the removed one. So when you have a fragment stack like this:

[A] [B]

FragmentManager knows, that is has to remove the [B] Fragment instance and add the [A] upon a pop operation. The problem is, that you replaced [B] with [C]:

[A] [C]

Unfortunately, the saved Fragment BackStack entry does not know about [C]. So during a pop operation, the FragmentManager will re-add [A], will not find [B] so it does nothing about that, and leave [C] where it is.

Solution

One possible solution for this problem is using child fragments. Fragment-A is a top-level Fragment, but Fragment-B and Fragment-C are child fragments of a WrapperFragment.

Basically, when you navigate to Fragment-B, you replace Fragment-A with the WrapperFragment in a saved transaction:

[A] [[B]]

Afterwards, when the user navigates to Fragment-C, you only replace the WrapperFragment's [B] with [C]:

[A] [[C]]

When the user presses the Back button, we'll correctly get back to Fragment-A:

[A]

Demo

I've assembled a GitHub project to demonstrate this technique.

like image 51
Zsombor Erdődy-Nagy Avatar answered Oct 07 '22 08:10

Zsombor Erdődy-Nagy


I can't figure out how you ended up with popping C fragments again and again without whole picture of what you did, but here what you got wrong about backstack:

  • BackStack entry actually saves the current state of whole FragmentManager, not just single fragment. I.e. if you perform multiple operations with multiple fragments in single a transaction and call addToBackStack(), you'll save the state of all fragments in the upcoming transaction.

  • When you call addToBackStack() you actually adding the next state to the backstack, i.e. actually, if you've called addToBackStack() when you've added A fragment, you actually have a state with B and a state with A behind it -- [A] [B], not [A] [C].

I recommend you to use this code and try everything with debugger looking at a state of FragmentManager object:

    int count = fragmentManager.getBackStackEntryCount();
    fragmentTransaction.addToBackStack(String.valueOf(count));

This way you'll be able to trace what's really going on in your app.

like image 41
Dmide Avatar answered Oct 07 '22 08:10

Dmide