4

In my open source Android app, an issue has been discovered where a specific fragment will come into view on top of another fragment or crash the app in a certain situation.

The issue on GitHub if you want to see more information and example screenshots: https://github.com/rpi-mobile/RPIMobile-Android/issues/31

I have pinned down the reason, but want to know which of the methods in the android.support.v4.app package to use to resolve the issue.

In MainActivity.java, is the code for the navigation drawer that uses FragmentTransaction.replace() to switch fragments.

The issue arises because in MapFragment, I use:

ViewMapFragment vmf = new ViewMapFragment();
FragmentTransaction ft = getSherlockActivity().getSupportFragmentManager().beginTransaction();
ft.addToBackStack(null);
ft.replace(R.id.content_frame, vmf);
ft.commit();

And in ViewMapFragment's onDestroyView():

FragmentManager fm = getSherlockActivity().getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.remove(fm.findFragmentById(R.id.mapview));
ft.commit();

The onDestroyView() properly removes the ViewMapFragment from view, but if you have it in view and use the navigation drawer to change to a different fragment, MapFragment is still on the back stack.

So, for my questions:

1) How do I check if a particular fragment is on the back stack before attempting to remove/replace it, or will the app not crash if you attempt when nothing is there (i.e. not checking)? E.g. calling popBackStack() when there is nothing on the back stack.

2) Should I use FragmentManager class methods to attempt to remove the MapFragment from the back stack or should I use FragmentTransaction methods? Pros vs cons?

3) What is the difference in the UI between popBackStack() and popBackStackImmediate()? Does the user see some glitchy transitions?

pdpiech
  • 43
  • 1
  • 4

1 Answers1

5

According to the documentation of FragmentTransaction, when you invoke addToBackStack method it just remembers what actions you perform in that transaction. When popBackStack method is invoked it will reverse those actions and execute them.

So, what happens:

  1. When we go from MapFragment to ViewMapFragment, FragmentManager remembers removing MapFragment and adding ViewMapFragment operations.
  2. Then we go to any other fragment using Navigation Drawer, which causes ViewMapFragment removing operation, and after that it adds fragment selected from Drawer.
  3. Finally, when we press Back button, popBackStackImmediate is invoked and FragmentManager executes reversed operations: ViewMapFragment is removed (actually it is removed already) and MapFragment is added. And here is the problem occurs - the selected fragment is still there, so we have two fragments on the screen at the same time.

There is several options to handle such a situation:

  1. Simply add Navigation Drawer operations to back stack.
  2. Clear back stack every time when fragment switching is started by Navigation Drawer.

To clear back stack you can use this code (related question):

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

If you put this line in the beginning of your selectItem method the bug will be fixed.

And your questions:

  1. You can check whether a fragment is in a back stack by using FragmentManager.findFragmentByTag method. See example in the end of my answer.
  2. An entry of back stack technically can contain several fragments, and I don't think there is a way to remove single fragment from it. Instead of removing you can just clear back stack (please, refer this question).
  3. The main difference is that popBackStack will give you a chance to do something, before it actually starts popping process (it will be started when application returns to it's event loop).

Example. For instance, we have added a fragment with tag "fragment-1":

getSupportFragmentManager()
        .beginTransaction()
        .replace(R.id.frame, new TestFragment(), "fragment-1")
        .commit();

Then, we put it into a back stack and replace it with another fragment:

getSupportFragmentManager()
        .beginTransaction()
        .addToBackStack(null)
        .replace(R.id.frame, new TestFragment(), "another-fragment")
        .commit();

At this point getSupportFragmentManager().findFragmentByTag("fragment-1") returns our first fragment (it gets it from a back stack entry). Now we can check whether this fragment is added to its activity by isAdded method - if it returns false, then we can make an assumption that the fragment is in a back stack.

Community
  • 1
  • 1
Ayzen
  • 933
  • 7
  • 10
  • Thank you for your answer! I will use your one line solution. As feedback on your answer, you didn't fully answer question 1 except indirectly in your answer to question 2 and your answer to 2 could be slightly more specific about where to look in the linked answer as I had to comb through the comments on that page. – pdpiech Sep 14 '14 at 00:13
  • Unfortunately there is no single good solution for this issue (it would be nice to have clearBackstack method in API, but there is no such method). I had to implement my own back stack system in my project due to this problem and transition glitches while clearing backstack (I'm using quite custom transition animations). :( – Ayzen Sep 14 '14 at 10:23
  • I have added more info regarding 1 & 2 questions. Hope this helps. – Ayzen Sep 14 '14 at 12:01