Подтвердить что ты не робот

Сохраненные фрагменты с UI и утечками памяти

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

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

4b9b3361

Ответ 1

В 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).

Ответ 2

Будьте внимательны при сохранении определенных объектов, связанных с активностью.

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

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

Ответ 3

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

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

Ответ 4

"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

Ответ 5

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

 @Override
public void onDestroy() {
    super.onDestroy();
    System.gc();
    System.gc();
}