What exactly happens when you call setRetainInstance(true)
on a Fragment
? The documentation is virtually non-existent and this seems like a very important function. Specifically I want to know how much of this sequence (that I made up) is true:
- The user rotates the device.
- The fragment is detached from the
Activity
andFragment.onDetach()
is called.- The activity is destroyed;
Activity.onDestroy()
is called.- The
Activity
java object is deleted (when possible, by the GC).- A new
Activity
java object is created; its constructor, andonCreate()
are called.- In
Activity.onCreate()
we either havesetContentView(...)
which sets a layout containing a fragment, or we useFragmentTransaction
to add a fragment.- I'm really not sure about this, but I assume that android is smart enough to find the old fragment, and call
Fragment.onAttach()
to reattach it to the newActivity
- Next (or before? who knows?)
Activity.onResume()
is called.
So is that correct? Is Android smart enough to find the old fragment, even if I explicitly use FragmentTransaction.add(new MyFragment(), ...)
the first time? And if so, how do I avoid adding another fragment in onCreate()
? Do I need to do something like this?:
if (getSupportFragmentManager().findFragmentByTag("foo") == null)
{
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(new FooFragment(), "foo").commit();
}
Ok, perhaps I was slightly too harsh on the Android documentation, because it does have some useful information, but sadly none of it is linked from
setRetainInstance()
. From the page about fragmentsThis strongly implies that if you do
setContentView(R.layout.whatever)
inActivity.onCreated()
and that layout contains a fragment withsetRetainInstance(true)
, then when the activity is recreated it will be searched for again using its id or tag.Secondly, for UI-less fragments, it states
And the docs link to a very good example -
FragmentRetainInstance.java
which I have reproduced below for your convenience. It does exactly what I speculated was the answer in my question (if (...findFragmentByTag() == null) { ...
).Finally, I created my own test activity to see exactly what functions are called. It outputs this, when you start in portrait and rotate to landscape. The code is below.
(This is edited a bit to make it easier to read.)
Note that the Android documentation is wrong: the UI-less fragment does receive a call to
onCreateView()
but it is free to returnnull
.Source code for
TestActivity
/TestFragment
Source code for
FragmentRetainInstance.java
(as of API 16):setRetainInstance()
inFragment
class is a clever replacement foronRetainCustomNonConfigurationInstance()
ofActivity
class, and more.Clearly stated in the documentation.
Here's Log of what happens (A UI fragment add on demand and then a config change):
Default
setRetainInstance(false)
So, Fragment is recreated whole new, and shown again, all this while
setRetainInstance(false)
And now with
setRetainInstance(true)
Noticed the effect ? Fragment instance (object 405288c0) was retained which is good. But the retained instance is very likely to hold resources and views and objects that belonged to previous orientations, which might lead to memory leaks.
Further care must be taken where you write the code to initiate this fragment: you must always check for pre-existing instance.
Moral of the story:
setRetainInstance()
is best used for non visual fragments.