When the FragmentPagerAdapter
adds a fragment to the FragmentManager, it uses a special tag based on the particular position that the fragment will be placed. FragmentPagerAdapter.getItem(int position)
is only called when a fragment for that position does not exist. After rotating, Android will notice that it already created/saved a fragment for this particular position and so it simply tries to reconnect with it with FragmentManager.findFragmentByTag()
, instead of creating a new one. All of this comes free when using the FragmentPagerAdapter
and is why it is usual to have your fragment initialisation code inside the getItem(int)
method.
Even if we were not using a FragmentPagerAdapter
, it is not a good idea to create a new fragment every single time in Activity.onCreate(Bundle)
. As you have noticed, when a fragment is added to the FragmentManager, it will be recreated for you after rotating and there is no need to add it again. Doing so is a common cause of errors when working with fragments.
A usual approach when working with fragments is this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
CustomFragment fragment;
if (savedInstanceState != null) {
fragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("customtag");
} else {
fragment = new CustomFragment();
getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "customtag").commit();
}
...
}
When using a FragmentPagerAdapter
, we relinquish fragment management to the adapter, and do not have to perform the above steps. By default, it will only preload one Fragment in front and behind the current position (although it does not destroy them unless you are using FragmentStatePagerAdapter
). This is controlled by ViewPager.setOffscreenPageLimit(int). Because of this, directly calling methods on the fragments outside of the adapter is not guaranteed to be valid, because they may not even be alive.
To cut a long story short, your solution to use putFragment
to be able to get a reference afterwards is not so crazy, and not so unlike the normal way to use fragments anyway (above). It is difficult to obtain a reference otherwise because the fragment is added by the adapter, and not you personally. Just make sure that the offscreenPageLimit
is high enough to load your desired fragments at all times, since you rely on it being present. This bypasses lazy loading capabilities of the ViewPager, but seems to be what you desire for your application.
Another approach is to override FragmentPageAdapter.instantiateItem(View, int)
and save a reference to the fragment returned from the super call before returning it (it has the logic to find the fragment, if already present).
For a fuller picture, have a look at some of the source of FragmentPagerAdapter (short) and ViewPager (long).
How do you as a developer use this
Call setRetainInstance(true)
. I typically do that in onCreateView()
or onActivityCreated()
, where I use it.
and why does it make things easier?
It tends to be simpler than onRetainNonConfigurationInstance()
for handling the retention of data across configuration changes (e.g., rotating the device from portrait to landscape). Non-retained fragments are destroyed and recreated on the configuration change; retained fragments are not. Hence, any data held by those retained fragments is available to the post-configuration-change activity.
Best Answer
First of all, check out my post on retained Fragments. It might help.
Now to answer your questions:
Yes, the
Fragment
's state will be retained across the configuration change. Specifically, "retained" means that the fragment will not be destroyed on configuration changes. That is, theFragment
will be retained even if the configuration change causes the underlyingActivity
to be destroyed.Just like
Activity
s,Fragment
s may be destroyed by the system when memory resources are low. Whether you have your fragments retain their instance state across configuration changes will have no effect on whether or not the system will destroy theFragment
s once you leave theActivity
. If you leave theActivity
(i.e. by pressing the home button), theFragment
s may or may not be destroyed. If you leave theActivity
by pressing the back button (thus, callingfinish()
and effectively destroying theActivity
), all of theActivity
s attachedFragment
s will also be destroyed.There are probably multiple reasons why it's not supported, but the most obvious reason to me is that the
Activity
holds a reference to theFragmentManager
, and theFragmentManager
manages the backstack. That is, no matter if you choose to retain yourFragment
s or not, theActivity
(and thus theFragmentManager
's backstack) will be destroyed on a configuration change. Another reason why it might not work is because things might get tricky if both retained fragments and non-retained fragments were allowed to exist on the same backstack.Retained fragments can be quite useful for propagating state information — especially thread management — across activity instances. For example, a fragment can serve as a host for an instance of
Thread
orAsyncTask
, managing its operation. See my blog post on this topic for more information.In general, I would treat it similarly to using
onConfigurationChanged
with anActivity
... don't use it as a bandaid just because you are too lazy to implement/handle an orientation change correctly. Only use it when you need to.