Понимание фрагмента setRetainInstance (логическое)

341

Начиная с документации:

public void setRetainInstance (логическое сохранение)

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

  • onDestroy () не будет вызываться (но onDetach () все равно будет, потому что фрагмент отсоединяется от текущей активности).
  • onCreate (Bundle) не будет вызываться, так как фрагмент не создается заново.
  • onAttach (Activity) и onActivityCreated (Bundle) будут по-прежнему вызываться.

У меня есть несколько вопросов:

  • Сохраняет ли фрагмент также свой вид, или он будет воссоздан при изменении конфигурации? Что именно означает «удерживается»?

  • Будет ли фрагмент уничтожен, когда пользователь покинет активность?

  • Почему он не работает с фрагментами на заднем стеке?

  • В каких случаях имеет смысл использовать этот метод?

Ixx
источник
4
похожий вопрос с хорошей информацией: зачем использовать Fragment # setRetainInstance (логическое значение)?
Ричард Ле Мезурье

Ответы:

348

Прежде всего, проверьте мой пост о сохраненных фрагментах. Это может помочь.

Теперь, чтобы ответить на ваши вопросы:

Сохраняет ли фрагмент также свое состояние просмотра , или это будет воссоздано при изменении конфигурации - что именно «сохраняется»?

Да, Fragmentсостояние будет сохраняться при изменении конфигурации. В частности, «сохраняется» означает, что фрагмент не будет уничтожен при изменении конфигурации. То есть, оно Fragmentбудет сохранено, даже если изменение конфигурации приведет Activityк разрушению базового объекта.

Будет ли фрагмент уничтожен, когда пользователь покинет активность?

Так же, как Activitys, Fragments может быть уничтожен системой, когда ресурсы памяти невелики. Сохраните ли ваши фрагменты свое состояние экземпляра при изменениях конфигурации, это не повлияет на то, будет ли система уничтожать Fragments после выхода из Activity. Если вы покинете Activity(т. Е. Нажмете кнопку «Домой»), то Fragments могут быть или не быть уничтожены. Если вы покинете Activityкнопку, нажав кнопку «назад» (таким образом, вызывая finish()и эффективно уничтожая Activity), все Activityприсоединенные Fragmentфайлы также будут уничтожены.

Почему он не работает с фрагментами на заднем стеке?

Вероятно, существует несколько причин, по которым он не поддерживается, но наиболее очевидная причина для меня заключается в том, что команда Activityсодержит ссылку на backstack FragmentManagerи FragmentManagerуправляет им. То есть, независимо от того, хотите ли вы сохранить свои Fragmentили нет, Activity(и, следовательно FragmentManager, обратный стек) будет уничтожен при изменении конфигурации. Другая причина, по которой это может не сработать, заключается в том, что все может стать сложнее, если разрешить существование как сохраненных фрагментов, так и не сохраненных фрагментов в одном и том же backstack.

В каких случаях имеет смысл использовать этот метод?

Сохраненные фрагменты могут быть весьма полезны для распространения информации о состоянии, особенно управления потоками, между экземплярами операций. Например, фрагмент может служить хостом для экземпляра Threadили AsyncTaskуправляющего его работой. Смотрите мой блог на эту тему для получения дополнительной информации.

В общем, я бы отнесся к этому аналогично использованию onConfigurationChangedс Activity... не используйте его как бандит, только потому, что вы слишком ленивы для правильной реализации / обработки изменения ориентации. Используйте его только тогда, когда вам нужно.

Алекс Локвуд
источник
37
Просмотр объектов не сохраняется, они всегда уничтожаются при изменении конфигурации.
Маркус Юнгингер
103
Насколько я могу сказать, если у вас есть setRetainInstance(true), на Fragmentобъект Java, и все его содержимое не уничтожены вращения, но вид будет воссоздан. Это onCreatedView()называется снова. Это в основном так, как это должно было работать с ActivitiesAndroid 1.0. Я не думаю, что это «ленивый» - использовать его или использовать его не совсем правильно. На самом деле я не могу понять, почему это не по умолчанию, или почему вы когда-нибудь захотите его отключить.
Тимммм
25
Я нахожу ваше объяснение: «Почему это не работает с фрагментами в заднем стеке?» трудно понять. Но, возможно, я тупой :(
HGPB
13
@dierre Деятельность может быть уничтожена разными способами. Например, если вы нажмете «назад», действие будет уничтожено. Если вы нажмете «домой», действие будет остановлено и в будущем может быть уничтожено при нехватке памяти. Сохраненные значения Fragmentсохраняются только при изменениях конфигурации, где основное действие должно быть уничтожено и немедленно воссоздано. Во всех других случаях, когда действие уничтожено, сохраненные фрагменты также будут уничтожены.
Алекс Локвуд
3
@AlexLockwood, не могли бы вы подтвердить следующее: Несмотря на то, что setRetainInstance(true)он используется, все же необходимо реализовать собственную устойчивость ( savedInstanceStateили иным образом), чтобы иметь возможность обрабатывать все сценарии: например, «home key, rotate, back to app» воссоздает мой фрагмент с помощью конструктора вызов, теряя все переменные состояния. У меня есть AsyncTaskпеременная-член, поэтому я хочу сохранить, теперь, если я хочу, чтобы она работала, я вынужден остановить задачу, сохранить состояние и возобновить работу, когда пользователь вернется. В общем, это всего лишь быстрый способ помочь с вращением, но в целом бесполезный.
TWiStErRob
28

setRetaininstanceполезно только в том случае, если ваша activityпапка уничтожена и воссоздана из-за изменения конфигурации, поскольку экземпляры сохраняются во время вызова onRetainNonConfigurationInstance. То есть, если вы поворачиваете устройство, сохраненные фрагменты останутся там (они не уничтожены и не воссозданы). Но когда среда выполнения убивает действие, чтобы вернуть ресурсы, ничего не остается. Когда вы нажимаете кнопку «Назад» и выходите из занятия, все разрушается.

Обычно я использую эту функцию, чтобы сохранить изменение ориентации Time. Скажем, я скачал несколько растровых изображений с сервера, и каждый из них занимает 1 МБ, когда пользователь случайно поворачивает свое устройство, я, конечно, не хочу делать всю работу по загрузке снова. Я создаю Fragmentудержание своих растровых изображений и добавляю его в диспетчер и вызываю setRetainInstance, все растровые изображения все еще там, даже если ориентация экрана меняется.

suitianshi
источник
Вы создаете фрагменты «Только для данных» (без какого-либо виджета), просто как держатель для ваших растровых изображений, или эти фрагменты также могут иметь виджеты? Я читал кое-что об опасности утечки памяти, когда фрагмент содержит что-то, связанное с контекстом / активностью ...
hgoebl
Рамки очистит mActivityссылку для вас. Но я не знаю, будет ли среда выполнения очищать виджеты в экземпляре фрагмента в этом случае. Пожалуйста, попробуйте или погрузитесь в исходный код.
suitianshi
Хороший пример того, когда мы можем использовать setRetaininstance
Mu Sa
12

SetRetainInstance (true) позволяет фрагменту выживать. Его члены будут сохранены во время изменения конфигурации, например, при ротации. Но это все еще может быть убито, когда деятельность убита в фоновом режиме. Если содержащая активность в фоновом режиме уничтожена системой, ее instanceState должен быть сохранен системой, которую вы правильно обработали наSaveInstanceState. Другими словами, всегда вызывается onSaveInstanceState. Хотя onCreateView не будет вызываться, если SetRetainInstance имеет значение true, а фрагмент / активность еще не уничтожен, он все равно будет вызываться, если он уничтожен и его пытаются вернуть обратно.

Вот некоторый анализ активности андроида / фрагмент, надеюсь, это поможет. http://ideaventure.blogspot.com.au/2014/01/android-activityfragment-life-cycle.html

Кеджун Ся
источник
8
Я определенно вижу, что onCreateView снова вызывается для сохраненного фрагмента при повороте экрана.
ау
Эта ссылка - ваш собственный блог? Вы должны ясно дать понять, если это так.
Флексо
4

setRetainInstance () - устарел

Как фрагменты Версия 1.3.0-alpha01

Метод setRetainInstance () для фрагментов устарел. С введением ViewModels разработчики получили специальный API для сохранения состояния, который можно связать с графиками действий, фрагментов и навигации. Это позволяет разработчикам использовать обычный, не сохраняемый фрагмент и сохранять отдельное состояние, которое они хотят сохранить отдельно, избегая общего источника утечек, сохраняя при этом полезные свойства одиночного создания и уничтожения сохраненного состояния (а именно, конструктор ViewModel и обратный вызов onCleared (), который он получает).

Гастон Сайлен
источник
2

setRetainInstance (boolean) полезен, когда вы хотите иметь какой-то компонент, который не связан с жизненным циклом Activity. Этот метод используется, например, rxloader для «обработки жизненного цикла активности Android для rxjava Observable» (который я нашел здесь ).

Мариан Падзиох
источник