Сохраненные фрагменты с пользовательским интерфейсом и утечками памяти

прочитал эту настройку.setOnRetainInstance(true) на фрагментах, представляющих пользовательский интерфейс, могут возникнуть утечки памяти.

Может ли кто-нибудь объяснить, почему и как это могло бы произойти? Я не'подробного объяснения нигде не найти.

 Snicolas16 нояб. 2012 г., 19:16
Просто для документирования темы, есть похожие темы:stackoverflow.com/q/11182180/693752
 Snicolas16 нояб. 2012 г., 19:16

Ответы на вопрос(5)

Вы можете переигратьonDestroy() и вызвать сборщик мусора.

 @Override
public void onDestroy() {
    super.onDestroy();
    System.gc();
    System.gc();
}
 Samuel Urbanowicz06 июл. 2016 г., 17:07
Вероятность 20%сработаю: P 2xSystem.gc() для безопасности? Никогда не полагайтесь наgc()

связанных с заданием.

Внимание: Несмотря на то, что вы можете вернуть любой объект, вы никогда не должны передавать объект, привязанный к Activity, такой какDrawable,адаптер,Посмотреть или любой другой объект, который 'связанные с контекстом. Если вы это сделаете, это приведет к утечке всех представлений и ресурсов исходного экземпляра действия. (Утечка ресурсов означает, что ваше приложение удерживает их, и они не могут быть собраны мусором, поэтому много памяти может быть потеряно.)

http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject

setRetainInstance(true) используется для сохранения экземпляров динамических фрагментов во время воссоздания Activity, таких как поворот экрана или другие изменения конфигурации. Это не означает, что фрагмент будет сохранен системой навсегда.

Когда действие прекращается по другим причинам, например, когда пользователь завершает действие (т. Е. Нажимает назад), фрагмент должен иметь право на сборку мусора.

Решение Вопроса

Fragment с пользовательским интерфейсом вы часто экономитеViews как состояние экземпляра для ускорения доступа. Например, ссылка на вашEditText так что ты недолженfindViewById это все время.

Проблема в том, чтоView держит ссылку наActivity контекст. Теперь, если вы сохранитеView ы также сохраняете ссылку на этот контекст.

Это не проблема, если контекст все еще действителен, но типичным случаем сохранения является перезапуск действия. Очень часто для поворота экрана, например. Активный отдых создаст новый контекст, а старые контексты предназначены для сбора мусора. Но это можетне будет мусора сейчас, так как ваш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, Пользовательский интерфейс не будет использоваться.

Исправление в этом примере просто меняетсяonDestroyView в

@Override
public void onDestroyView() {
    super.onDestroyView();
    mLeak = null; // now cleaning up!
}

И помимо ведения ссылок наViews вы, очевидно, не должны хранить ссылки наActivity (например, изonAttach - чистить наonDetach) или любойContext (если это несApplication контекст).

 Tamas08 авг. 2013 г., 13:09
Есть идеи, как справиться, гора нулевых указателей это может вызвать? У меня есть несколько слушателей анимации, потоков, и каждый использует одну из ссылок, которая обнуляется в onDestroyView. Поэтому всякий раз, когда я использую одну из этих ссылок, я сначала должен проверить на нулевое значение. Тот'очень неудобно.
 Jannie Theunissen12 авг. 2013 г., 20:48
Отличный гид запл. Просто придурок: из документов -onCreate(Bundle) will not be called since the fragment is not being re-created. Так что бесполезно переопределять этот метод.
 Happy Dev24 окт. 2014 г., 00:48
@zapl, так что тогда каждому вызову представления должна предшествовать нулевая проверка? потому что у меня есть проблемы, когда onClickListener вызывается после onDestroyView ...
 zapl24 окт. 2014 г., 11:09
Слушатель кликов @Happydev никогда не должен иметь такой проблемы, потому что больше нет кнопок, которые нужно нажимать после уничтожения представления. Если у вас есть асинхронные задачи или другие события, которые каким-то образом задерживаются, вам придется проверить, есть ли в настоящее время какой-либо пользовательский интерфейс для изменения. Если нет, сохраните состояние result / new в переменной и позвольте следующему onCreateView использовать его для обновления пользовательского интерфейса.
 Happy Dev23 сент. 2014 г., 07:21
@zapl, какой смысл делать ручную очистку переменных, созданных в onCreateView в onDestroyView, когда onCreateView будет вызываться снова через несколько миллисекунд, и перезаписывать эти ссылки новыми объектами, чтобы старые объекты не имели ссылки и были GC?
 zapl08 мар. 2014 г., 15:52
Это'в моем опыте также проще, если вы незаботиться о случаях и просто всегда убирать. Существуют симметричные методы создания / уничтожения, поэтому фреймворк должен думать, а вам нужно просто реализовать правильную вещь.
 zapl12 авг. 2013 г., 20:56
@JannieT "жизненный цикл будет немного отличаться, когда активностьвоссоздал "-onCreate будет по-прежнему вызываться при первоначальном создании фрагмента. И вам нужно переопределить это, если вы хотите сделать что-то, что происходит только один раз при создании, напримерsetRetainInstance так как вам нужно сделать это только один раз.
 zapl08 авг. 2013 г., 15:05
Пример @Tamas:pastebin.com/8A18kMym вам в основном нужно размножаться /onAttachonDetach ко всему, что относится к контексту, и /onCreateViewonDestroyView ко всему, что содержит ссылки на представления.
 Tamas08 авг. 2013 г., 15:23
setRetainInstance (true) не рекомендуется imho, я нене хочу ничего хранить в памяти, если этоне видно Кроме того, адаптеры на самом деле не проблема. Я'мы много работали с обезьяной, и она обычно терпела неудачу в следующих частях:pastebin.com/9Gx3McWz редактировать: обновленная ссылка
 zapl08 авг. 2013 г., 15:37
@Tamas retain instance - это хорошо, если вы знаете, что фрагмент будет скоро использоваться повторно, как фрагменты во вкладках, после поворота ... Что касается вашей ссылки: что должно произойти, если текстового представления больше нет, поскольку представление исчезло ? Вы'Вам нужно будет либо убить задачу, либо приостановить ее, либо позволить ей проверить, что что-то есть, и продолжить в следующий раз. Если вы продолжаете использовать одно и то же текстовое представление, вы устанавливаете текст для невидимого текстового представления, которое 'просто хранится в памяти без причины. И да, это больно сAsyncTask
 Pedro Lopes08 мар. 2014 г., 13:50
В первом примере яМне просто интересно, как плохо не очистить представление onDestroyView. Я предполагаю, что есть два случая: onConfigChange, поэтому вызывается onDestroy, за которым следует onCreateView (который будет сбрасывать переменную mLeak), и когда действие завершается, фрагмент отсоединяется и уничтожается. Но я понимаю вашу точку зрения, и, кажется, лучше просто никогда не сохранять какие-либо взгляды
 zapl08 мар. 2014 г., 15:45
@PedroLopes взять, например, фрагменты вViewPager, Пейджер будет содержать до 3 фрагментов (один видимый и один справа, один слева) с видами. Если вы переверните страницу, она будетonDestroyView фрагмент, который 'больше не в пределах досягаемости. Если этоView содержит большое изображение вImageView Вы теряете много памяти, хотя это изображение не будет использоваться повторно. В следующий раз, когда тебя это пугаетпридется создать новый черезonCreateView, течьContext через вращение будет вытекать все, кроме 1View может быть уже слишком большим и привести кOutOfMemoryError
 Diederik04 июл. 2016 г., 07:40
Установка ссылки на мои представления на ноль не имела никакого значения для моей утечки активности (которая содержала фрагменты). Была другая проблема.
 zapl22 окт. 2014 г., 14:10
@Happydev не гарантируется, что будетonCreateView сразу послеonDestroyView, Приложения могут переходить в фоновый режим, фрагменты могут быть скрыты, ... Очистка в уничтожении гарантирует, что вы 'из-за этого не заполняем память ненужными вещами и потенциально исключениями.
 zapl08 авг. 2013 г., 14:47
@Tamas Listeners, Threads, ... все в порядке, чтобы на них ссылались, пока они нехранить ссылку на все, что есть или ссылается наActivity, Если на них есть что-то подобное, и действие воссоздается с использованием этого действия, это не приведет к чему-либо действительному, поэтому вам все равно придется его обновить.

"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

Ваш ответ на вопрос