Retained Fragments with UI and memory leaks
I've read that setting .setOnRetainInstance(true)
on fragments presenting UI may lead to memory leaks.
Could somebody please explain why and how this would happen? I didn't find a detailed explanation anywhere.
In a Fragment
with UI you often save some View
s as instance state to speed up access. For example a link to your EditText
so you don't have to findViewById
it all the time.
The problem is that a View
keeps a reference to the Activity
context. Now if you retain a View
you also retain a reference to that context.
That is no problem if the context is still valid but the typical retain case is restarting the Activity. Very often for a screen rotation for example. Activity recreation will create a new context and old contexts are intended to be garbage collected. But it can't be garbage collected now since your Fragment
still has a reference to the old one.
Following example shows how not to do it
public class LeakyFragment extends Fragment {
private View mLeak; // retained
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mLeak = inflater.inflate(R.layout.whatever, container, false);
return mLeak;
}
@Override
public void onDestroyView() {
super.onDestroyView();
// not cleaning up.
}
}
To get rid of that problem, you need to clear all references to your UI in onDestroyView
. Once the Fragment
instance is re-used you will be asked to create a new UI on onCreateView
. There is also no point in keeping the UI after onDestroyView
. The Ui is not going to be used.
The fix in this example is just changing onDestroyView
to
@Override
public void onDestroyView() {
super.onDestroyView();
mLeak = null; // now cleaning up!
}
And besides keeping references to View
s you should obviously not keep references to the Activity
(e.g. from onAttach
- clean on onDetach
) or any Context
(unless it's the Application
context).
setRetainInstance(true)
is used to retain instances of dynamic Fragments during an Activity recreation, such as a screen rotation or other config changes. This does not mean the Fragment will be retained forever by the system though.
When an Activity is terminated for other reasons, such as the user finishing the Activity (i.e. pressing back), the Fragment should be eligible for garbage collection.