Le composant Arch de navigation peut-il créer une fuite de mémoire faussement positive?

14

J'ai une connaissance de base des fuites de mémoire et de ce qui peut les provoquer. C'est pourquoi je ne comprends pas si j'ai un problème dans mon code ou s'il s'agit d'un faux positif. Je ne sais pas quelle partie du code je dois partager car le projet n'est pas petit. Mais faites le moi savoir dans les commentaires et j'ajouterai le code requis.

J'utilise le composant d'arche de navigation et suis le modèle MVVM. J'ai ajouté la bibliothèque LeakCanary plus tard dans le développement du projet et elle a immédiatement commencé à me donner des avertissements sur les instances conservées lorsque je navigue entre les écrans.

Le problème se produit lorsque j'ajoute des fragments à la pile arrière. Avec chaque fragment ajouté à la pile arrière, le compteur des instances conservées augmente. Lorsqu'il atteint la valeur seuil de 5, LeakCanary vide le tas et fournit un rapport.

Mais si je clique sur le bouton Retour et reviens aux écrans précédents, le compteur des instances conservées diminue et finalement, une fois retourné au 1er écran, toutes les instances retenues disparaissent.

Si je regarde les rapports d'analyse de tas, cela dit que la variable coordinatorLayout, qui est une référence au CoordinatorLayoutXML, a fui. Si je supprime la variable et toute son utilisation et que je lance à nouveau l'application, je vois le même problème, mais maintenant avec une autre variable qui fait référence à une autre vue en xml. J'ai essayé de supprimer toutes les vues et leur utilisation que LeakCanary a signalées comme fuyant. Quand il a dit que un TextView, qui est juste utilisé pour placer un texte onViewCreatedet qui n'est utilisé nulle part ailleurs, fuit, j'ai commencé à douter qu'il y ait un problème dans mon code.

J'ai analysé les appels de méthode du cycle de vie en fragments et j'ai remarqué que lorsque je navigue vers un nouvel écran pour le fragment précédent, toutes les méthodes jusqu'à et y compris onDestroyViewsont appelées mais pas onDestroy. Lorsque je clique en arrière, onDestroyun fragment est appelé au-dessus de la pile arrière et le compteur d'instances conservées diminue.

Je soupçonne que le composant Navigation conserve l'instance d'un fragment lorsqu'il est dans la pile arrière et que LeakCanary le considère comme une fuite.

Marat
la source

Réponses:

24

C'est ainsi que fonctionnent les fragments de la pile arrière (et la navigation utilise uniquement les API de fragment existantes): la vue du fragment est détruite, mais le fragment lui-même n'est pas détruit - ils sont conservés CREATEDjusqu'à ce que vous appuyiez sur le bouton de retour et reveniez au fragment (après quoi onCreateView()sera rappelé et vous remonterez à RESUMED).

Selon les Fragments: Passé, Présent et Futur , l'un des changements futurs à venir aux Fragments est une option opt-in pour détruire les Fragments sur la pile arrière, plutôt que d'avoir deux cycles de vie distincts. Ce n'est pas encore disponible.

Vous devez annuler vos références aux vues dans onDestroyViewcar c'est le signe que la vue n'est plus utilisée par le système Fragment et qu'elle peut être récupérée en toute sécurité si ce n'était pour votre référence continue à la vue.

ianhanniballake
la source
2
Android View Binding résout-il ce problème? Je ne trouve aucune documentation indiquant si la référence aux vues View Binding (peut-être l'objet de liaison lui-même) est automatiquement «annulée» onDestroyViewavec View Binding.
Tim Malseed
3
@TimMalseed - vous devez annuler vous-même votre référence à l'objet de liaison, rien ne se passe automatiquement.
ianhanniballake
1
@Emmanuel - vous devez déposer votre référence à l'objet de liaison lui-même car il contient une référence dure aux vues qu'il possède.
ianhanniballake
1
@Emmanuel - vous pouvez toujours déposer une demande de fonctionnalité !
ianhanniballake
1
@Emmanuel - Je pense que ce serait certainement un changement de comportement (ce qui pourrait impliquer qu'il s'agisse d'un indicateur opt in séparé), mais avoir le bon LifecycleOwner serait suffisant pour résoudre un ensemble de problèmes de mémoire.
ianhanniballake