After popping the backstack, my Fragments are not being garbage collected? Their onStop() methods are called, but not their onDestroyView() or onDestroy() methods.
How can I ensure that their onDestory() methods are called?
I made the mistake of making a "God Activity", and all my fragments .replace() a single container FrameLayout. Let's say I have four fragments, FragA, FragB, FragC, and FragD, with a backstack that looks something like this:
FragA -> FragB -> FragC -> FragD
FragD is the only visible fragment. Then, I call .popBackstack()`
FragA -> FragB -> FragC =!= FragD
FragD has been detached, and FragC is now visible to the user. When try to instantiate a new FragD (from FragC), it's created, and there are two instances of FragD on my heap. And the first FragD instance's onDestroyView() is not called.
How do I fix this?
EDIT: here's an example of how I am currecntly replacing fragments in my UI:
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new SearchScreenFragment())
.addToBackStack(null)
.commit();
EDIT2: onDetach() isn't being called either.
I should have been using this: getChildFragmentManager():
From https://stackoverflow.com/a/23500935/891242
Related
I have FragmentA and FragmentB.
I added FragmentA to an Activity using the add (not replace) fragment transaction. A button in FragmentA makes a callback to it's Activity which then makes an add fragment transaction to add FragmentB.
I update the title of the ActionBar with the value of a variable (which is fetched online) in FragmentA and FragmentB.
After the fragment transactions I outlined above, if I'm in FragmentB and I press the back button (now I'm in FragmentA), the title of the ActionBar is still that of FragmentB.
So I wanted to know which lifecycle methods are called on FragmentA when I'm coming from FragmentB so I can update the Actionbar from there.
I don't know if there is a method that is called when backstack is popped, but what you can do is overwrite onBackPressed in your Activity class that contains the fragments, call fragmentManager.popBackStackImmediate. This method returns a boolean, if it's true, then you went from fragmentB to fragmentA (or to any other previous fragment). You are in your activity class, so you can update your ActionBar anyway you'd like.
One more thing, if fragmentManager.popBackStackImmediate is true, don't call super.onBackPressed()!!!
onStart() method is clearly the adapted one. When comming from backstack, it will always be called.
If I have an Activity and I add fragment1, hide fragment1, add fragment2, hide fragment2, then use
fragmentManager.beginTransaction()
.replace(R.id.content, fragment3)
.commit();
to now replace in fragment3, what happens to fragment1 and fragment2? Do they get destroyed or something or are they still there? (this is all within the same container, R.id.content)
Unless you add the transaction to backstack, all the the fragments in the container are getting destroyed
If the fragment was added to the back stack, the fragments object instances will have onDestroyView() called, and the data required to reconstruct the fragment is still part of the fragment back stack, so that when fragments are popped off the stack, the prior fragments can be reconstituted and displayed again (similar to activities that may have been destroyed in the activity stack). If the fragment was not part of the back stack, it is simply destroyed because the user can never come back to it.
I have one activity, ActivityA, and 3 fragments, FragmentA, FragmentB, and FragmentC.
To add FragmentA, I use replace, where fragment is a new instance of FragmentA:
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout_fragment, fragment)
.commit();
In the onCreate method of ActivityA, I do a check for FragmentA creation:
if (savedInstanceState == null) {
FragmentA fragmentA = FragmentA.newInstance();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout_fragment, fragmentA)
.commit();
}
To add FragmentB and FragmentC, I use add and addToBackStack, where fragment is either a new instance of FragmentB or FragmentC:
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frame_layout_fragment, fragment)
.addToBackStack(null)
.commit();
I press the back button while on FragmentC, it shows FragmentB, and pressing the back button while on FragmentB shows FragmentA, as expected. While I was on FragmentC, I rotate the device, and FragmentC still shows, as expected.
However, when I navigate from FragmentC to FragmentA using the 2 presses of the back button, then rotate the device twice, portrait -> landscape -> portrait, add FragmentB, add FragmentC, then I rotate the device once, I expected FragmentC to show, but instead FragmentB shows. When I press the back button once, nothing happens. When I press it again, it navigates back to FragmentA. It seems like FragmentC is present in the back stack, but for some reason its not visible.
Why is FragmentC not visible in this scenario?
I think this answer to another question is your answer too: Difference between add(), replace(), and addToBackStack()
It says:
One more importance difference between add and replace is: replace
removes the existing fragment and adds a new fragment. This means when
you press back button the fragment that got replaced will be created
with its onCreateView being invoked. Whereas add retains the existing
fragments and adds a new fragment that means existing fragment will be
active and they wont be in 'paused' state hence when a back button is
pressed onCreateView is not called for the existing fragment(the
fragment which was there before new fragment was added). In terms of
fragment's life cycle events onPause, onResume, onCreateView and other
life cycle events will be invoked in case of replace but they wont be
invoked in case of add.
I have two fragments which share the same spot on a activity. When activity starts fragA is displayed. When you click a button from fragA it gets pushed to backstack and replaced by fragB. When a button from fragB is pressed the fragB is destroyed and fragA is poped from the backstack but it should also change some views based on what button from fragB was pressed (set the text for a textview for example). I used interfaces for fragment-activity communication. Everything goes well but the views from fragA don't want to change. I think I'm trying to change them while the fragment is in backstack.
#Override
public void onFragBClick(Bundle bundle) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragA = (FragA) fragmentManager.findFragmentByTag(TAG_FRAG_A);
if (fragA != null) {
fragmentManager.popBackStack(TAG_FRAG_A_BACKSTACK,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragA.onFragBSignalReceived(bundle);
}
}
This is the method that handles communication. Everything seems fine in logs, onFragBSignalReceived gets called, but I can't change the state of fragA's views in it. My guess is that when it gets called fragA is still on the backstack and that's why views are not updated. In that case how can I achieve this behavior in some other way?
You might want to use popBackStackImmediate() instead of popBackStack(). popBackStackImmediate() will perform the operation inside the method, whereas popBackStack() will queue the action and perform it later on.
I am very confused between these functions and their purposes. What I have observed that using replace() replaces the existing fragment with a new one. We can use addToBackStack(null) to put that fragment in back stack so we can go back to the previously shown fragment. Now when a fragment is added (or replaced) - onAttach() -> onCreate() etc.... methods of the fragment are called in order.
Now when we call remove() on the fragment from our activity, which functions of the fragment are called and in which order?
What does attach() and detach() do? Does detach() remove the fragment? And when these two attach() and detach() are used, which functions of the fragment are called and in which order??
Also, what happens on popBackStack()?? I mean which functions are called when we use popBackStack()on the fragment from our activity??
And when does onDestroy() called??
Thank you.
Now when we call remove() on the fragment from our activity, which functions of the fragment are called and in which order?
Look at http://developer.android.com/reference/android/app/Fragment.html .
The order is: onPause(), onStop(), onDestroyView(), onDestroy(), onDetach()
What does attach() and detach() do? Does detach() remove the fragment? And when these two attach() and detach() are used, which functions of the fragment are called and in which order??
attach() and detach() is respectively associates or detaches the Fragment with/from the Activity. When attaching the Fragment, the onAttach() lifecycle method is called, when detaching, the onDetach() lifecycle method is called in the Fragment. For more information look at the link above.
Also, what happens on popBackStack()?? I mean which functions are called when we use popBackStack()on the fragment from our activity??
If the Fragment hasn't been destroyed, then on popBackStack() the onStart() and onResume() methods are called. If the Fragment has been destroyed previously, then the lifecycle methods will be called starting from onAttach(). It's the same as, when you press the back button on Activities.
Just a note on popBackStack(). It doesn't pop a fragment, it pops a fragment transaction. So whatever the last fragment transaction was is reversed. If you were displaying a FragmentA and your transaction was:
fragmentTransaction.replace(R.id.your_layout, fragmentB);
fragmentTransaction.addToBackStack(null);
It would replace FragmentA with FragmentB, and add that transaction (not the fragment) to the back stack. If you then hit the back button, it pops the back stack and gets the transaction, which was "replace this FragmentA with a FragmentB". It then reverses that transaction. Backwards, the instruction is to replace whatever the current fragment is with FragmentA. If the original FragmentA still exists, it uses that one. If it's been destroyed, it makes a new one.
Suppose fragment A and fragment B was added to a container with the following steps:
1. Added fragment A => .replace(R.id.container, fragmentA) => addToBackStack(null)
2. Added fragment B => .replace(R.id.container, fragmentB) => addToBackStack(null)
3. Removed fragment B => fragmentManager.popBackStack();
Callbacks when fm.popBackStack() is called:
FragmentB: onPause()
FragmentB: onStop()
FragmentB: onDestroy()
FragmentB: onDetach()
FragmentA: onCreateView()
FragmentA: onViewCreated()
FragmentA: onActivityCreated()
FragmentA: onStart()
FragmentA: onResume()
Explanation: Why while removing and destroying fragment B using popBackStack(), fragment A view was created again and resumed?
Ans: When you added fragment B, you used replace() and addToBackStack(), so all the fragments were removed from the container, and fragment B was added to the container. And, also this transaction was recorded in the Back stack. So, when fm.popBackStack() is called, first the transaction is popped out from the back stack and so the operations reverted itself, therefore fragment b is removed from the container and destroyed. And all other fragments get added, for our case fragment A's view is added to the container. Also noted that fragment A's onAttach & onCreate() is not called because it has already been created & attached to the MainActivity earlier.
Back press on fragment B does the same thing:
If you press the back button, it calls fm.popBackStack() and pops the transaction.