I've faced with an issue with Nested Fragments in Android. When I rotate the screen the Nested Fragments survive somehow. I've come up with a sample example to illustrate this issue.
public class ParentFragment extends BaseFragment
{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_parent, container);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager()
.beginTransaction()
.add(getId(), new ParentFragmentChild(), ParentFragmentChild.class.getName())
.commit();
}
@Override
public void onResume() {
super.onResume();
log.verbose("onResume(), numChildFragments: " + getChildFragmentManager().getFragments().size());
}
}
public class ParentFragmentChild extends BaseFragment
{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_child, null);
}
}
BaseFragment just logs method calls. This is what I see when I rotate the screen.
ParentFragment﹕ onAttach(): ParentFragment{420d0a98 #0 id=0x7f060064}
ParentFragment﹕ onCreate()
ParentFragment﹕ onViewCreated()
ParentFragmentChild﹕ onAttach(): ParentFragmentChild{420d08d0 #0 id=0x7f060064 com.kinoteatr.ua.filmgoer.test.ParentFragmentChild}
ParentFragmentChild﹕ onCreate()
ParentFragmentChild﹕ onViewCreated()
ParentFragment﹕ onResume()
ParentFragment﹕ onResume(), numChildFragments: 1
ParentFragmentChild﹕ onResume()
ParentFragmentChild﹕ onPause()
ParentFragment﹕ onPause()
ParentFragment﹕ onSaveInstanceState()
ParentFragmentChild﹕ onSaveInstanceState()
ParentFragmentChild﹕ onStop()
ParentFragment﹕ onStop()
ParentFragmentChild﹕ onDestroyView()
ParentFragment﹕ onDestroyView()
ParentFragmentChild﹕ onDestroy()
ParentFragmentChild﹕ onDetach()
ParentFragment﹕ onDestroy()
ParentFragment﹕ onDetach()
ParentFragment﹕ onAttach(): ParentFragment{4211bc38 #0 id=0x7f060064}
ParentFragment﹕ onCreate()
ParentFragmentChild﹕ onAttach(): ParentFragmentChild{420f4180 #0 id=0x7f060064 com.kinoteatr.ua.filmgoer.test.ParentFragmentChild}
ParentFragmentChild﹕ onCreate()
ParentFragment﹕ onViewCreated()
ParentFragmentChild﹕ onViewCreated()
ParentFragmentChild﹕ onAttach(): ParentFragmentChild{42132a08 #1 id=0x7f060064 com.kinoteatr.ua.filmgoer.test.ParentFragmentChild}
ParentFragmentChild﹕ onCreate()
ParentFragmentChild﹕ onViewCreated()
ParentFragment﹕ onResume()
ParentFragment﹕ onResume(), numChildFragments: 2
ParentFragmentChild﹕ onResume()
ParentFragmentChild﹕ onResume()
ParentFragmentChild﹕ onPause()
ParentFragmentChild﹕ onPause()
ParentFragment﹕ onPause()
ParentFragment﹕ onSaveInstanceState()
ParentFragmentChild﹕ onSaveInstanceState()
ParentFragmentChild﹕ onSaveInstanceState()
ParentFragmentChild﹕ onStop()
ParentFragmentChild﹕ onStop()
ParentFragment﹕ onStop()
ParentFragmentChild﹕ onDestroyView()
ParentFragmentChild﹕ onDestroyView()
ParentFragment﹕ onDestroyView()
ParentFragmentChild﹕ onDestroy()
ParentFragmentChild﹕ onDetach()
ParentFragmentChild﹕ onDestroy()
ParentFragmentChild﹕ onDetach()
ParentFragment﹕ onDestroy()
ParentFragment﹕ onDetach()
ParentFragment﹕ onAttach(): ParentFragment{42122a48 #0 id=0x7f060064}
ParentFragment﹕ onCreate()
ParentFragmentChild﹕ onAttach(): ParentFragmentChild{420ffd48 #0 id=0x7f060064 com.kinoteatr.ua.filmgoer.test.ParentFragmentChild}
ParentFragmentChild﹕ onCreate()
ParentFragmentChild﹕ onAttach(): ParentFragmentChild{420fffa0 #1 id=0x7f060064 com.kinoteatr.ua.filmgoer.test.ParentFragmentChild}
ParentFragmentChild﹕ onCreate()
ParentFragment﹕ onViewCreated()
ParentFragmentChild﹕ onViewCreated()
ParentFragmentChild﹕ onViewCreated()
ParentFragmentChild﹕ onAttach(): ParentFragmentChild{42101488 #2 id=0x7f060064 com.kinoteatr.ua.filmgoer.test.ParentFragmentChild}
ParentFragmentChild﹕ onCreate()
ParentFragmentChild﹕ onViewCreated()
ParentFragment﹕ onResume()
ParentFragment﹕ onResume(), numChildFragments: 3
ParentFragmentChild﹕ onResume()
ParentFragmentChild﹕ onResume()
ParentFragmentChild﹕ onResume()
They keep getting multiplied. Does anybody know why is that ?
When I rotate the screen the Nested Fragments survive somehow.
They survive for the same reason the ParentFragment
survives even when you are not
retaining the instance with setRetainInstance()
. The FragmentManager
is that reason,
in this case the ChildFragmentManager
the ParentFragment
uses to handle the nested fragments.
Some things you need to know:
FragmentManager
is responsible for managing the fragments and
adding them to the activity's view hierarchy.FragmentManager
handles two things:
Fragment Transactions
.FragmentManager
for a fragment using the container
view id (findFragmentById
), if the fragment is already in the list,
the FragmentManager
will return it. Then you can use it.When Activity initially appears
Since the app just started, you only have one instance of each fragment. One of ParentFragment
and one of ParentFragmentChild
. So far so good.
Screen rotation #1
At this point, the Activity's FragmentManager
and the ParentFragment's ChildFragmentManager
save its list of fragments. Then, you can see how both fragments are completely destroyed.
When the Activity is recreated, the new FragmentManager
s retrieve the list and
recreate the listed fragments to make everything as it was before the orientation change. Note that are not the same instances, these are new fragments recreated by Android (that's why you can't have a Fragment without the empty constructor, Android needs it to recreate the fragments).
Now, this is the code inside your ParentFragment
:
getChildFragmentManager()
.beginTransaction()
.add(getId(), new ParentFragmentChild(), ParentFragmentChild.class.getName())
.commit();
You are not trying to find out if ChildFragmentManager
already has a ParentFragmentChild
in its list (which already has). That's the reason you can see two fragments being created, the first one is being recreated by the ChildFragmentManager
, the second is created by the previous code.
Screen rotation #2
There were two ParentChildFragment
in the ChildFragmentManager's list before the second orientation change, they were recreated and one more is created by the code.
It's better to use the fragments the FragmentManager
recreates instead of creating new ones.