Comprendre setRetainInstance de Fragment (booléen)

341

En commençant par la documentation:

public void setRetainInstance (booléen retenir)

Contrôlez si une instance de fragment est conservée lors de la recréation d'activité (par exemple à partir d'un changement de configuration). Cela ne peut être utilisé qu'avec des fragments qui ne sont pas dans la pile arrière. S'il est défini, le cycle de vie du fragment sera légèrement différent lorsqu'une activité est recréée:

  • onDestroy () ne sera pas appelé (mais onDetach () le sera toujours, car le fragment est détaché de son activité actuelle).
  • onCreate (Bundle) ne sera pas appelé car le fragment n'est pas en cours de recréation.
  • onAttach (Activity) et onActivityCreated (Bundle) seront toujours appelés.

J'ai quelques questions:

  • Le fragment conserve-t-il également sa vue ou sera-t-il recréé lors d'un changement de configuration? Que signifie exactement "retenu"?

  • Le fragment sera-t-il détruit lorsque l'utilisateur quittera l'activité?

  • Pourquoi ne fonctionne-t-il pas avec des fragments sur la pile arrière?

  • Quels sont les cas d'utilisation où il est logique d'utiliser cette méthode?

Ixx
la source
4
question similaire avec de bonnes informations: pourquoi utiliser Fragment # setRetainInstance (booléen)?
Richard Le Mesurier

Réponses:

348

Tout d'abord, consultez mon article sur les fragments conservés. Ça pourrait aider.

Maintenant pour répondre à vos questions:

Le fragment conserve-t-il également son état d' affichage , ou sera-t-il recréé lors d'un changement de configuration - qu'est-ce qui est "conservé" exactement?

Oui, l' Fragmentétat de sera conservé tout au long du changement de configuration. Plus précisément, «conservé» signifie que le fragment ne sera pas détruit lors des changements de configuration. Autrement dit, le Fragmentsera conservé même si le changement de configuration entraîne la Activitydestruction du sous-jacent .

Le fragment sera-t-il détruit lorsque l'utilisateur quittera l'activité?

Tout comme Activitys, Fragments peut être détruit par le système lorsque les ressources mémoire sont faibles. Le fait que vos fragments conservent leur état d'instance malgré les modifications de configuration n'aura aucun effet sur le fait que le système détruira ou non les Fragments une fois que vous aurez quitté leActivity . Si vous quittez le Activity(c'est-à-dire en appuyant sur le bouton d'accueil), le Fragments peut être détruit ou non. Si vous quittez le Activityen appuyant sur le bouton de retour (donc, en appelant finish()et en détruisant efficacement le Activity), tous les Activitys attachésFragment seront également détruits.

Pourquoi ne fonctionne-t-il pas avec des fragments sur la pile arrière?

Il y a probablement plusieurs raisons pour lesquelles ce n'est pas pris en charge, mais la raison la plus évidente pour moi est que le Activitycontient une référence au FragmentManager, et leFragmentManager gère le backstack. Autrement dit, peu importe si vous choisissez de conserver votre Fragments ou non, le Activity(et donc le FragmentManagerbackstack du) sera détruit lors d'un changement de configuration. Une autre raison pour laquelle cela pourrait ne pas fonctionner est que les choses pourraient devenir délicates si les fragments conservés et les fragments non conservés pouvaient exister sur le même backstack.

Quels sont les cas d'utilisation où il est logique d'utiliser cette méthode?

Les fragments conservés peuvent être très utiles pour propager des informations d'état - en particulier la gestion des threads - entre les instances d'activité. Par exemple, un fragment peut servir d'hôte pour une instance deThread ou AsyncTask, gérer son fonctionnement. Voir mon article de blog sur ce sujet pour plus d'informations.

En général, je le traiterais de la même manière onConfigurationChangedqu'avec un Activity... ne l'utilisez pas comme un bandaid juste parce que vous êtes trop paresseux pour implémenter / gérer correctement un changement d'orientation. Utilisez-le uniquement lorsque vous en avez besoin.

Alex Lockwood
la source
37
Les objets de vue ne sont pas conservés, ils sont toujours détruits lors des changements de configuration.
Markus Junginger
103
Autant que je sache, si vous l'avez setRetainInstance(true), l' Fragmentobjet java et tout son contenu ne sont pas détruits lors de la rotation, mais la vue est recréée. C'est onCreatedView()appelé à nouveau. C'est essentiellement la façon dont il aurait dû fonctionner Activitiesdepuis Android 1.0. Je ne pense pas que ce soit "paresseux" de l'utiliser, ou l'utiliser n'est pas "approprié". En fait, je ne vois pas pourquoi ce n'est pas la valeur par défaut, ni pourquoi vous voudriez la désactiver.
Timmmm
25
Je trouve votre explication pour "Pourquoi cela ne fonctionne-t-il pas avec des fragments sur la pile arrière?" difficile à comprendre. Mais peut-être que je suis stupide :(
HGPB
13
@dierre Une activité peut être détruite de plusieurs façons. Par exemple, si vous cliquez sur "retour", l'activité sera détruite. Si vous cliquez sur "home", l'activité sera arrêtée et à un moment donné dans le futur, elle pourrait être détruite lorsque la mémoire est insuffisante. Les Fragments conservés ne sont conservés qu'à travers les changements de configuration, où l'activité sous-jacente doit être détruite et recréée immédiatement. Dans tous les autres cas où l'activité est détruite, les fragments conservés seront également détruits.
Alex Lockwood
3
@AlexLockwood pouvez - vous s'il vous plaît confirmer ce qui suit: Même si l' setRetainInstance(true)on utilise, on encore doit mettre en œuvre leur propre persistance ( savedInstanceStateou non) être capable de gérer tous les scénarios: par exemple , « clé de la maison, rotate, retour à l' application » reconstitue mon fragment avec le constructeur appel, en perdant toutes les variables d'état. J'ai une AsyncTaskvariable en tant que membre, c'est pourquoi je veux conserver, maintenant, si je veux qu'elle fonctionne, je suis obligé d'arrêter la tâche, d'enregistrer l'état et de reprendre lorsque l'utilisateur revient. Donc, dans l'ensemble, ce n'est qu'un moyen rapide d'aider à la rotation, mais sinon inutile en général.
TWiStErRob
28

setRetaininstancen'est utile que lorsque votre activityest détruit et recréé en raison d'un changement de configuration car les instances sont enregistrées lors d'un appel àonRetainNonConfigurationInstance . Autrement dit, si vous faites pivoter l'appareil, les fragments conservés y resteront (ils ne sont pas détruits et recréés.) Mais lorsque le runtime tue l'activité pour récupérer des ressources, il ne reste plus rien. Lorsque vous appuyez sur le bouton de retour et quittez l'activité, tout est détruit.

Habituellement, j'utilise cette fonction pour enregistrer l'heure de changement d'orientation. Dites que j'ai téléchargé un tas de bitmaps depuis le serveur et chacun fait 1 Mo, lorsque l'utilisateur fait accidentellement pivoter son appareil, je ne veux certainement pas refaire tout le travail de téléchargement. Je crée une Fragmentconservation de mes bitmaps et l'ajoute au gestionnaire et appelle setRetainInstance, tous les Bitmaps sont toujours là même si l'orientation de l'écran change.

suitianshi
la source
Créez-vous des fragments "Data-Only" (sans widget) juste comme support pour vos bitmaps ou ces fragments peuvent-ils également avoir des widgets? J'ai lu quelque chose sur le danger de produire des fuites de mémoire lorsque le fragment contient quelque chose lié au contexte / activité ...
hgoebl
Le cadre effacera la mActivityréférence pour vous. Mais je ne sais pas si le runtime effacerait également les widgets dans l'instance de fragment dans ce cas. Veuillez l'essayer ou plonger dans le code source.
suitianshi
Bel exemple de l'utilisation de setRetaininstance
Mu Sa
12

SetRetainInstance (true) permet au fragment de survivre. Ses membres seront conservés lors du changement de configuration comme la rotation. Mais il peut toujours être tué lorsque l'activité est tuée en arrière-plan. Si l'activité conteneur en arrière-plan est supprimée par le système, son instanceState doit être enregistré correctement par le système que vous avez géré sur SaveInstanceState. Dans un autre mot, onSaveInstanceState sera toujours appelé. Bien que onCreateView ne soit pas appelé si SetRetainInstance est vrai et que le fragment / l'activité n'est pas encore tué, il sera toujours appelé s'il est tué et en cours de tentative de retour.

Voici quelques analyses de l'activité / fragment android espérant que cela aide. http://ideaventure.blogspot.com.au/2014/01/android-activityfragment-life-cycle.html

Kejun Xia
la source
8
Je vois définitivement onCreateView être appelé à nouveau sur le fragment conservé lors de la rotation de l'écran.
aij
Ce lien est-il votre propre blog? Vous devriez le préciser si c'est le cas.
Flexo
4

setRetainInstance () - Obsolète

As Fragments Version 1.3.0-alpha01

La méthode setRetainInstance () sur les fragments est obsolète. Avec l'introduction de ViewModels, les développeurs disposent d'une API spécifique pour conserver l'état qui peut être associée aux graphiques des activités, des fragments et de la navigation. Cela permet aux développeurs d'utiliser un fragment normal non conservé et de conserver séparément l'état spécifique qu'ils souhaitent conserver, en évitant une source commune de fuites tout en conservant les propriétés utiles d'une création unique et de la destruction de l'état conservé (à savoir, le constructeur du ViewModel et le rappel onCleared () qu'il reçoit).

Gastón Saillén
la source
2

setRetainInstance (boolean) est utile lorsque vous souhaitez avoir un composant qui n'est pas lié au cycle de vie de l'activité. Cette technique est utilisée par exemple par rxloader pour "gérer le cycle de vie d'activité d'Android pour l'Observable de rxjava" (que j'ai trouvé ici ).

Marian Paździoch
la source