Я читал, что установка .setOnRetainInstance(true)
на фрагменты, представляющие пользовательский интерфейс, может привести к утечке памяти.
Может кто-нибудь объяснить, почему и как это произойдет? Я нигде не нашел подробного объяснения.
Я читал, что установка .setOnRetainInstance(true)
на фрагменты, представляющие пользовательский интерфейс, может привести к утечке памяти.
Может кто-нибудь объяснить, почему и как это произойдет? Я нигде не нашел подробного объяснения.
В Fragment
с пользовательским интерфейсом вы часто сохраняете некоторое View
как состояние экземпляра, чтобы ускорить доступ. Например, ссылка на ваш EditText
, поэтому вам не нужно findViewById
все это время.
Проблема заключается в том, что a View
содержит ссылку на контекст Activity
. Теперь, если вы сохраните View
, вы также сохраните ссылку на этот контекст.
Это не проблема, если контекст все еще действителен, но типичный случай сохранения перезапуска Activity. Очень часто для вращения экрана, например. Активность отдыха создаст новый контекст, и старые контексты предназначены для сбора мусора. Но теперь это не может быть сбор мусора, так как ваш Fragment
все еще имеет ссылку на старый.
В следующем примере показано, как не делать это
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.
}
}
Чтобы избавиться от этой проблемы, вам нужно очистить все ссылки на свой интерфейс в onDestroyView
. После повторного использования экземпляра Fragment
вам будет предложено создать новый интерфейс на onCreateView
. Также нет смысла поддерживать пользовательский интерфейс после onDestroyView
. Ui не будет использоваться.
Исправление в этом примере просто меняет onDestroyView
на
@Override
public void onDestroyView() {
super.onDestroyView();
mLeak = null; // now cleaning up!
}
Кроме того, чтобы сохранить ссылки на View
, вы, очевидно, не должны ссылаться на Activity
(например, из onAttach
- clean on onDetach
) или любой Context
(если это не контекст Application
).
Будьте внимательны при сохранении определенных объектов, связанных с активностью.
Предостережение: Пока вы можете вернуть какой-либо объект, вы никогда не должны передавать объект, привязанный к Activity, например Drawable, Adapter, Вид или любой другой объект, связанный с контекстом. Если вы это сделаете, это приведет к утечке всех представлений и ресурсов исходного экземпляра действия. (Утечка ресурсов означает, что ваше приложение поддерживает их, и они не могут быть собраны в мусор, поэтому можно потерять много памяти.)
http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject
setRetainInstance(true)
используется для сохранения экземпляров динамических фрагментов во время активного отдыха, таких как поворот экрана или другие изменения конфигурации. Это не означает, что Fragment будет сохраняться навсегда системой.
Когда действие прекращается по другим причинам, например, для пользователя, выполняющего действие (т.е. отмена), фрагмент должен иметь право на сбор мусора.
"setRetainInstance" используется для поддержания состояния фрагмента при воссоздании активности. Согласно официальной документации: если мы используем "setRetainInstance", 2 метода жизненного цикла фрагмента не будут выполняться (onCreate, onDestroy). Однако мнения, содержащиеся в фрагменте, будут воссозданы, и это связано с тем, что жизненный цикл будет выполнен из "onCreateView". В этих случаях, если мы сохранили некоторые данные в "onSaveInstanceState", мы должны запросить его в "onActivityCreated", а не в "onCreate".
Официальная информация: https://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)
Дополнительная информация: https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en
вы можете переопределить onDestroy()
и вызвать сборщик мусора.
@Override
public void onDestroy() {
super.onDestroy();
System.gc();
System.gc();
}