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.
Related
I'm having problem with fragment. Lets try to understand my issue, I have two fragment A and B. When app start with main activity,i start fragment A as you can see :
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new MusicFragment())
.commit();
When i click on a button, it starts fragment B
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new BarFragment())
.addToBackStack(null)
.commit();
Main problem is after starting fragment B,when i pressed back to go back to fragment A , Fragment A Recreated with new state.
I don' want to recreate fragment A. I only want to start fragment from old state where i left. How to fix it ?
Instead of calling the replace method you should be calling the add method with a subsequent call to addToBackStack with an argument of null. The add method adds the fragement to the stack with a null tag and calling the addToBackStack with an argument of null then the current fragment is stopped upon commit. If the method is not called then the current fragment is destroyed and recreated when coming back.
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container,new BarFragment())
.addToBackStack(null)
.commit();
You can clearly find it in the documentation quote saying this:
If you don't call addToBackStack() when you perform a transaction that
removes a fragment, then that fragment is destroyed when the
transaction is committed and the user cannot navigate back to it.
Whereas, if you do call addToBackStack() when removing a fragment,
then the fragment is stopped and is later resumed if the user
navigates back.
Here are some points that need to be taken care while creating fragments.
Check the backstack if the fragment is already created.
If it was created previously pop it from the backstack and put it on top so that it is visible to the user.
If fragment is not present in backstack crate it and store it in backstack.
Create a method like below which will handle such situation.
private void openFragment(Fragment fragment_to_be_opened){
String fragment_to_be_opened_name = fragment_to_be_opened.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
// fetching the fragment if it is present in backstack
boolean fragment_allready_present = manager.popBackStackImmediate (fragment_to_be_opened_name, 0);
if (!fragment_allready_present){ //fragment is not present in backstack so create it and save the name in //backstack
FragmentTransaction fragment_trasition = manager.beginTransaction();
fragment_trasition.replace(R.id.fragment_container, fragment_to_be_opened);
fragment_trasition.addToBackStack(fragment_to_be_opened_name);
fragment_trasition.commit();
}
}
Now call this method from the activity to open a new fragment like
// create instance of fragment and pass it to the open fragment method
Fragment myFragment = new myFragment();
openFragment(myFragment);
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.
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
I try to replace Fragment A by a Fragment B but the onDestroy() method is not called on the Fragment B.
Broadcast receivers are unregistered and threads are killed before replacing the fragment.
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transac = manager.beginTransaction();
transac.replace(R.id.content_fragment_A, fragmentB, myTAG);
transac.commit();
Why is my Fragment A not destroyed ?
A Fragment's onDestroy() is called when the Activity's onDestroy() is called. In your scenario you are just replacing Fragments but you didn't destroy your Activity yet. If you want to check on this try to destroy your Activity after you replace your Fragment, both fragmentA and fragmentB onDestroy() should be called.
I would look more into the fragment activity lifcycle
http://developer.android.com/guide/components/fragments.html#Lifecycle
When are adding the new fragment to an existing ViewGroup, for replace function:
Replace an existing fragment that was added to a container. This is
essentially the same as calling remove(Fragment) for all currently
added fragments that were added with the same containerViewId and then
add(int, Fragment, String) with the same arguments given here.
and a remove
Remove an existing fragment. If it was added to a container, its view
is also removed from that container.
From this terminology, I assume that onDestroyView() is being called and not necessary onDestory right away.